diff options
Diffstat (limited to 'ANDROID_3.4.5/sound/pci/hda')
26 files changed, 0 insertions, 49031 deletions
diff --git a/ANDROID_3.4.5/sound/pci/hda/Kconfig b/ANDROID_3.4.5/sound/pci/hda/Kconfig deleted file mode 100644 index 163b6b5d..00000000 --- a/ANDROID_3.4.5/sound/pci/hda/Kconfig +++ /dev/null @@ -1,261 +0,0 @@ -menuconfig SND_HDA_INTEL - tristate "Intel HD Audio" - select SND_PCM - select SND_VMASTER - select SND_KCTL_JACK - 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_PREALLOC_SIZE - int "Pre-allocated buffer size for HD-audio driver" - range 0 32768 - default 64 - help - Specifies the default pre-allocated buffer-size in kB for the - HD-audio driver. A larger buffer (e.g. 2048) is preferred - for systems using PulseAudio. The default 64 is chosen just - for compatibility reasons. - - Note that the pre-allocation size can be changed dynamically - via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too. - -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_INPUT_BEEP_MODE - int "Digital beep registration mode (0=off, 1=on, 2=mute sw on/off)" - depends on SND_HDA_INPUT_BEEP=y - default "1" - range 0 2 - help - Set 0 to disable the digital beep interface for HD-audio by default. - Set 1 to always enable the digital beep interface for HD-audio by - default. Set 2 to control the beep device registration to input - layer using a "Beep Switch" in mixer applications. - -config SND_HDA_INPUT_JACK - bool "Support jack plugging notification via input layer" - depends on INPUT=y || INPUT=SND - select SND_JACK - help - Say Y here to enable the jack plugging notification via - input layer. - -config SND_HDA_PATCH_LOADER - bool "Support initialization patch loading for HD-audio" - depends on EXPERIMENTAL - select FW_LOADER - select SND_HDA_HWDEP - select SND_HDA_RECONFIG - help - Say Y here to allow the HD-audio driver to load a pseudo - firmware file ("patch") for overriding the BIOS setup at - start up. The "patch" file can be specified via patch module - option, such as patch=hda-init. - - This option turns on hwdep and reconfig features automatically. - -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_ENABLE_REALTEK_QUIRKS - bool "Build static quirks for Realtek codecs" - depends on SND_HDA_CODEC_REALTEK - default y - help - Say Y here to build the static quirks codes for Realtek codecs. - If you need the "model" preset that the default BIOS auto-parser - can't handle, turn this option on. - - If your device works with model=auto option, basically you don't - need the quirk code. By turning this off, you can reduce the - module size quite a lot. - -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_HDMI - bool "Build HDMI/DisplayPort HD-audio codec support" - select SND_DYNAMIC_MINORS - default y - help - Say Y here to include HDMI and DisplayPort HD-audio codec - support in snd-hda-intel driver. This includes all AMD/ATI, - Intel and Nvidia HDMI/DisplayPort codecs. - - When the HD-audio driver is built as a module, the codec - support code is also built as another module, - snd-hda-codec-hdmi. - This module is automatically loaded at probing. - -config SND_HDA_CODEC_CIRRUS - bool "Build Cirrus Logic codec support" - depends on SND_HDA_INTEL - default y - help - Say Y here to include Cirrus Logic codec support in - snd-hda-intel driver, such as CS4206. - - When the HD-audio driver is built as a module, the codec - support code is also built as another module, - snd-hda-codec-cirrus. - This module is automatically loaded at probing. - -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_CA0110 - bool "Build Creative CA0110-IBG codec support" - depends on SND_HDA_INTEL - default y - help - Say Y here to include Creative CA0110-IBG codec support in - snd-hda-intel driver, found on some Creative X-Fi cards. - - When the HD-audio driver is built as a module, the codec - support code is also built as another module, - snd-hda-codec-ca0110. - This module is automatically loaded at probing. - -config SND_HDA_CODEC_CA0132 - bool "Build Creative CA0132 codec support" - depends on SND_HDA_INTEL - default y - help - Say Y here to include Creative CA0132 codec 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-ca0132. - 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" - depends on PM - 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/ANDROID_3.4.5/sound/pci/hda/Makefile b/ANDROID_3.4.5/sound/pci/hda/Makefile deleted file mode 100644 index ace157cc..00000000 --- a/ANDROID_3.4.5/sound/pci/hda/Makefile +++ /dev/null @@ -1,65 +0,0 @@ -snd-hda-intel-objs := hda_intel.o - -snd-hda-codec-y := hda_codec.o hda_jack.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_HWDEP) += hda_hwdep.o -snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o - -# for trace-points -CFLAGS_hda_codec.o := -I$(src) - -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-cirrus-objs := patch_cirrus.o -snd-hda-codec-ca0110-objs := patch_ca0110.o -snd-hda-codec-ca0132-objs := patch_ca0132.o -snd-hda-codec-conexant-objs := patch_conexant.o -snd-hda-codec-via-objs := patch_via.o -snd-hda-codec-hdmi-objs := patch_hdmi.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_CIRRUS -obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-cirrus.o -endif -ifdef CONFIG_SND_HDA_CODEC_CA0110 -obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-ca0110.o -endif -ifdef CONFIG_SND_HDA_CODEC_CA0132 -obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-ca0132.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_HDMI -obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-hdmi.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/ANDROID_3.4.5/sound/pci/hda/hda_beep.c b/ANDROID_3.4.5/sound/pci/hda/hda_beep.c deleted file mode 100644 index 60738e52..00000000 --- a/ANDROID_3.4.5/sound/pci/hda/hda_beep.c +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Digital Beep Input Interface for HD-audio codec - * - * Author: Matthew Ranostay <mranostay@embeddedalley.com> - * Copyright (c) 2008 Embedded Alley Solutions Inc - * - * 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/input.h> -#include <linux/pci.h> -#include <linux/slab.h> -#include <linux/workqueue.h> -#include <linux/export.h> -#include <sound/core.h> -#include "hda_beep.h" -#include "hda_local.h" - -enum { - DIGBEEP_HZ_STEP = 46875, /* 46.875 Hz */ - DIGBEEP_HZ_MIN = 93750, /* 93.750 Hz */ - DIGBEEP_HZ_MAX = 12000000, /* 12 KHz */ -}; - -static void snd_hda_generate_beep(struct work_struct *work) -{ - struct hda_beep *beep = - container_of(work, struct hda_beep, beep_work); - struct hda_codec *codec = beep->codec; - - if (!beep->enabled) - return; - - /* generate tone */ - snd_hda_codec_write(codec, beep->nid, 0, - AC_VERB_SET_BEEP_CONTROL, beep->tone); -} - -/* (non-standard) Linear beep tone calculation for IDT/STAC codecs - * - * The tone frequency of beep generator on IDT/STAC codecs is - * defined from the 8bit tone parameter, in Hz, - * freq = 48000 * (257 - tone) / 1024 - * that is from 12kHz to 93.75Hz in steps of 46.875 Hz - */ -static int beep_linear_tone(struct hda_beep *beep, int hz) -{ - if (hz <= 0) - return 0; - hz *= 1000; /* fixed point */ - hz = hz - DIGBEEP_HZ_MIN - + DIGBEEP_HZ_STEP / 2; /* round to nearest step */ - if (hz < 0) - hz = 0; /* turn off PC beep*/ - else if (hz >= (DIGBEEP_HZ_MAX - DIGBEEP_HZ_MIN)) - hz = 1; /* max frequency */ - else { - hz /= DIGBEEP_HZ_STEP; - hz = 255 - hz; - } - return hz; -} - -/* HD-audio standard beep tone parameter calculation - * - * The tone frequency in Hz is calculated as - * freq = 48000 / (tone * 4) - * from 47Hz to 12kHz - */ -static int beep_standard_tone(struct hda_beep *beep, int hz) -{ - if (hz <= 0) - return 0; /* disabled */ - hz = 12000 / hz; - if (hz > 0xff) - return 0xff; - if (hz <= 0) - return 1; - return hz; -} - -static int snd_hda_beep_event(struct input_dev *dev, unsigned int type, - unsigned int code, int hz) -{ - struct hda_beep *beep = input_get_drvdata(dev); - - switch (code) { - case SND_BELL: - if (hz) - hz = 1000; - case SND_TONE: - if (beep->linear_tone) - beep->tone = beep_linear_tone(beep, hz); - else - beep->tone = beep_standard_tone(beep, hz); - break; - default: - return -1; - } - - /* schedule beep event */ - schedule_work(&beep->beep_work); - return 0; -} - -static void snd_hda_do_detach(struct hda_beep *beep) -{ - input_unregister_device(beep->dev); - beep->dev = NULL; - cancel_work_sync(&beep->beep_work); - /* turn off beep for sure */ - snd_hda_codec_write(beep->codec, beep->nid, 0, - AC_VERB_SET_BEEP_CONTROL, 0); -} - -static int snd_hda_do_attach(struct hda_beep *beep) -{ - struct input_dev *input_dev; - struct hda_codec *codec = beep->codec; - int err; - - input_dev = input_allocate_device(); - if (!input_dev) { - printk(KERN_INFO "hda_beep: unable to allocate input device\n"); - return -ENOMEM; - } - - /* setup digital beep device */ - input_dev->name = "HDA Digital PCBeep"; - input_dev->phys = beep->phys; - input_dev->id.bustype = BUS_PCI; - - input_dev->id.vendor = codec->vendor_id >> 16; - input_dev->id.product = codec->vendor_id & 0xffff; - input_dev->id.version = 0x01; - - input_dev->evbit[0] = BIT_MASK(EV_SND); - input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE); - input_dev->event = snd_hda_beep_event; - input_dev->dev.parent = &codec->bus->pci->dev; - input_set_drvdata(input_dev, beep); - - err = input_register_device(input_dev); - if (err < 0) { - input_free_device(input_dev); - printk(KERN_INFO "hda_beep: unable to register input device\n"); - return err; - } - beep->dev = input_dev; - return 0; -} - -static void snd_hda_do_register(struct work_struct *work) -{ - struct hda_beep *beep = - container_of(work, struct hda_beep, register_work); - - mutex_lock(&beep->mutex); - if (beep->enabled && !beep->dev) - snd_hda_do_attach(beep); - mutex_unlock(&beep->mutex); -} - -static void snd_hda_do_unregister(struct work_struct *work) -{ - struct hda_beep *beep = - container_of(work, struct hda_beep, unregister_work.work); - - mutex_lock(&beep->mutex); - if (!beep->enabled && beep->dev) - snd_hda_do_detach(beep); - mutex_unlock(&beep->mutex); -} - -int snd_hda_enable_beep_device(struct hda_codec *codec, int enable) -{ - struct hda_beep *beep = codec->beep; - enable = !!enable; - if (beep == NULL) - return 0; - if (beep->enabled != enable) { - beep->enabled = enable; - if (!enable) { - /* turn off beep */ - snd_hda_codec_write(beep->codec, beep->nid, 0, - AC_VERB_SET_BEEP_CONTROL, 0); - } - if (beep->mode == HDA_BEEP_MODE_SWREG) { - if (enable) { - cancel_delayed_work(&beep->unregister_work); - schedule_work(&beep->register_work); - } else { - schedule_delayed_work(&beep->unregister_work, - HZ); - } - } - return 1; - } - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_enable_beep_device); - -int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) -{ - struct hda_beep *beep; - - if (!snd_hda_get_bool_hint(codec, "beep")) - return 0; /* disabled explicitly by hints */ - if (codec->beep_mode == HDA_BEEP_MODE_OFF) - return 0; /* disabled by module option */ - - beep = kzalloc(sizeof(*beep), GFP_KERNEL); - if (beep == NULL) - return -ENOMEM; - snprintf(beep->phys, sizeof(beep->phys), - "card%d/codec#%d/beep0", codec->bus->card->number, codec->addr); - /* enable linear scale */ - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_DIGI_CONVERT_2, 0x01); - - beep->nid = nid; - beep->codec = codec; - beep->mode = codec->beep_mode; - codec->beep = beep; - - INIT_WORK(&beep->register_work, &snd_hda_do_register); - INIT_DELAYED_WORK(&beep->unregister_work, &snd_hda_do_unregister); - INIT_WORK(&beep->beep_work, &snd_hda_generate_beep); - mutex_init(&beep->mutex); - - if (beep->mode == HDA_BEEP_MODE_ON) { - int err = snd_hda_do_attach(beep); - if (err < 0) { - kfree(beep); - codec->beep = NULL; - return err; - } - } - - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_attach_beep_device); - -void snd_hda_detach_beep_device(struct hda_codec *codec) -{ - struct hda_beep *beep = codec->beep; - if (beep) { - cancel_work_sync(&beep->register_work); - cancel_delayed_work(&beep->unregister_work); - if (beep->dev) - snd_hda_do_detach(beep); - codec->beep = NULL; - kfree(beep); - } -} -EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device); diff --git a/ANDROID_3.4.5/sound/pci/hda/hda_beep.h b/ANDROID_3.4.5/sound/pci/hda/hda_beep.h deleted file mode 100644 index 55f06474..00000000 --- a/ANDROID_3.4.5/sound/pci/hda/hda_beep.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Digital Beep Input Interface for HD-audio codec - * - * Author: Matthew Ranostay <mranostay@embeddedalley.com> - * Copyright (c) 2008 Embedded Alley Solutions Inc - * - * 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 - */ - -#ifndef __SOUND_HDA_BEEP_H -#define __SOUND_HDA_BEEP_H - -#include "hda_codec.h" - -#define HDA_BEEP_MODE_OFF 0 -#define HDA_BEEP_MODE_ON 1 -#define HDA_BEEP_MODE_SWREG 2 - -/* beep information */ -struct hda_beep { - struct input_dev *dev; - struct hda_codec *codec; - unsigned int mode; - char phys[32]; - int tone; - hda_nid_t nid; - unsigned int enabled:1; - unsigned int request_enable:1; - unsigned int linear_tone:1; /* linear tone for IDT/STAC codec */ - struct work_struct register_work; /* registration work */ - struct delayed_work unregister_work; /* unregistration work */ - struct work_struct beep_work; /* scheduled task for beep event */ - struct mutex mutex; -}; - -#ifdef CONFIG_SND_HDA_INPUT_BEEP -int snd_hda_enable_beep_device(struct hda_codec *codec, int enable); -int snd_hda_attach_beep_device(struct hda_codec *codec, int nid); -void snd_hda_detach_beep_device(struct hda_codec *codec); -#else -static inline int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) -{ - return 0; -} -static inline void snd_hda_detach_beep_device(struct hda_codec *codec) -{ -} -#endif -#endif diff --git a/ANDROID_3.4.5/sound/pci/hda/hda_codec.c b/ANDROID_3.4.5/sound/pci/hda/hda_codec.c deleted file mode 100644 index 926b4553..00000000 --- a/ANDROID_3.4.5/sound/pci/hda/hda_codec.c +++ /dev/null @@ -1,5548 +0,0 @@ -/* - * Universal Interface for Intel High Definition Audio Codec - * - * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> - * - * - * 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/mm.h> -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <linux/pci.h> -#include <linux/mutex.h> -#include <linux/module.h> -#include <sound/core.h> -#include "hda_codec.h" -#include <sound/asoundef.h> -#include <sound/tlv.h> -#include <sound/initval.h> -#include <sound/jack.h> -#include "hda_local.h" -#include "hda_beep.h" -#include "hda_jack.h" -#include <sound/hda_hwdep.h> - -#define CREATE_TRACE_POINTS -#include "hda_trace.h" - -/* - * vendor / preset table - */ - -struct hda_vendor_id { - unsigned int id; - const char *name; -}; - -/* codec vendor labels */ -static struct hda_vendor_id hda_vendor_ids[] = { - { 0x1002, "ATI" }, - { 0x1013, "Cirrus Logic" }, - { 0x1057, "Motorola" }, - { 0x1095, "Silicon Image" }, - { 0x10de, "Nvidia" }, - { 0x10ec, "Realtek" }, - { 0x1102, "Creative" }, - { 0x1106, "VIA" }, - { 0x111d, "IDT" }, - { 0x11c1, "LSI" }, - { 0x11d4, "Analog Devices" }, - { 0x13f6, "C-Media" }, - { 0x14f1, "Conexant" }, - { 0x17e8, "Chrontel" }, - { 0x1854, "LG" }, - { 0x1aec, "Wolfson Microelectronics" }, - { 0x434d, "C-Media" }, - { 0x8086, "Intel" }, - { 0x8384, "SigmaTel" }, - {} /* terminator */ -}; - -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); -static void hda_keep_power_on(struct hda_codec *codec); -#define hda_codec_is_power_on(codec) ((codec)->power_on) -#else -static inline void hda_keep_power_on(struct hda_codec *codec) {} -#define hda_codec_is_power_on(codec) 1 -#endif - -/** - * snd_hda_get_jack_location - Give a location string of the jack - * @cfg: pin default config value - * - * Parse the pin default config value and returns the string of the - * jack location, e.g. "Rear", "Front", etc. - */ -const char *snd_hda_get_jack_location(u32 cfg) -{ - static char *bases[7] = { - "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); - -/** - * snd_hda_get_jack_connectivity - Give a connectivity string of the jack - * @cfg: pin default config value - * - * Parse the pin default config value and returns the string of the - * jack connectivity, i.e. external or internal connection. - */ -const char *snd_hda_get_jack_connectivity(u32 cfg) -{ - static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" }; - - return jack_locations[(cfg >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3]; -} -EXPORT_SYMBOL_HDA(snd_hda_get_jack_connectivity); - -/** - * snd_hda_get_jack_type - Give a type string of the jack - * @cfg: pin default config value - * - * Parse the pin default config value and returns the string of the - * jack type, i.e. the purpose of the jack, such as Line-Out or CD. - */ -const char *snd_hda_get_jack_type(u32 cfg) -{ - static char *jack_types[16] = { - "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; - - if ((codec->addr & ~0xf) || (direct & ~1) || (nid & ~0x7f) || - (verb & ~0xfff) || (parm & ~0xffff)) { - printk(KERN_ERR "hda-codec: out of range cmd %x:%x:%x:%x:%x\n", - codec->addr, direct, nid, verb, parm); - return ~0; - } - - val = (u32)codec->addr << 28; - val |= (u32)direct << 27; - val |= (u32)nid << 20; - val |= verb << 8; - val |= parm; - return val; -} - -/* - * Send and receive a verb - */ -static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd, - unsigned int *res) -{ - struct hda_bus *bus = codec->bus; - int err; - - if (cmd == ~0) - return -1; - - if (res) - *res = -1; - again: - snd_hda_power_up(codec); - mutex_lock(&bus->cmd_mutex); - trace_hda_send_cmd(codec, cmd); - err = bus->ops.command(bus, cmd); - if (!err && res) { - *res = bus->ops.get_response(bus, codec->addr); - trace_hda_get_response(codec, *res); - } - mutex_unlock(&bus->cmd_mutex); - snd_hda_power_down(codec); - if (res && *res == -1 && bus->rirb_error) { - if (bus->response_reset) { - snd_printd("hda_codec: resetting BUS due to " - "fatal communication error\n"); - trace_hda_bus_reset(bus); - bus->ops.bus_reset(bus); - } - goto again; - } - /* clear reset-flag when the communication gets recovered */ - if (!err) - bus->response_reset = 0; - return err; -} - -/** - * snd_hda_codec_read - send a command and get the response - * @codec: the HDA codec - * @nid: NID to send the command - * @direct: direct flag - * @verb: the verb to send - * @parm: the parameter for the verb - * - * Send a single command and read the corresponding response. - * - * Returns the obtained response value, or -1 for an error. - */ -unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, - int direct, - unsigned int verb, unsigned int parm) -{ - unsigned cmd = make_codec_cmd(codec, nid, direct, verb, parm); - unsigned int res; - if (codec_exec_verb(codec, cmd, &res)) - return -1; - return res; -} -EXPORT_SYMBOL_HDA(snd_hda_codec_read); - -/** - * snd_hda_codec_write - send a single command without waiting for response - * @codec: the HDA codec - * @nid: NID to send the command - * @direct: direct flag - * @verb: the verb to send - * @parm: the parameter for the verb - * - * Send a single command without waiting for response. - * - * Returns 0 if successful, or a negative error code. - */ -int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct, - unsigned int verb, unsigned int parm) -{ - unsigned int cmd = make_codec_cmd(codec, nid, direct, verb, parm); - unsigned int res; - return codec_exec_verb(codec, cmd, - codec->bus->sync_write ? &res : NULL); -} -EXPORT_SYMBOL_HDA(snd_hda_codec_write); - -/** - * snd_hda_sequence_write - sequence writes - * @codec: the HDA codec - * @seq: VERB array to send - * - * Send the commands sequentially from the given array. - * The array must be terminated with NID=0. - */ -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 - * @codec: the HDA codec - * @nid: NID to parse - * @start_id: the pointer to store the start NID - * - * Parse the NID and store the start NID of its sub-nodes. - * Returns the number of sub-nodes. - */ -int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t *start_id) -{ - unsigned int parm; - - parm = snd_hda_param_read(codec, nid, AC_PAR_NODE_COUNT); - if (parm == -1) - return 0; - *start_id = (parm >> 16) & 0x7fff; - return (int)(parm & 0x7fff); -} -EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes); - -/* look up the cached results */ -static hda_nid_t *lookup_conn_list(struct snd_array *array, hda_nid_t nid) -{ - int i, len; - for (i = 0; i < array->used; ) { - hda_nid_t *p = snd_array_elem(array, i); - if (nid == *p) - return p; - len = p[1]; - i += len + 2; - } - return NULL; -} - -/** - * snd_hda_get_conn_list - get connection list - * @codec: the HDA codec - * @nid: NID to parse - * @listp: the pointer to store NID list - * - * Parses the connection list of the given widget and stores the list - * of NIDs. - * - * Returns the number of connections, or a negative error code. - */ -int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid, - const hda_nid_t **listp) -{ - struct snd_array *array = &codec->conn_lists; - int len, err; - hda_nid_t list[HDA_MAX_CONNECTIONS]; - hda_nid_t *p; - bool added = false; - - again: - /* if the connection-list is already cached, read it */ - p = lookup_conn_list(array, nid); - if (p) { - if (listp) - *listp = p + 2; - return p[1]; - } - if (snd_BUG_ON(added)) - return -EINVAL; - - /* read the connection and add to the cache */ - len = snd_hda_get_raw_connections(codec, nid, list, HDA_MAX_CONNECTIONS); - if (len < 0) - return len; - err = snd_hda_override_conn_list(codec, nid, len, list); - if (err < 0) - return err; - added = true; - goto again; -} -EXPORT_SYMBOL_HDA(snd_hda_get_conn_list); - -/** - * snd_hda_get_connections - copy connection list - * @codec: the HDA codec - * @nid: NID to parse - * @conn_list: connection list array - * @max_conns: max. number of connections to store - * - * Parses the connection list of the given widget and stores the list - * of NIDs. - * - * Returns the number of connections, or a negative error code. - */ -int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t *conn_list, int max_conns) -{ - const hda_nid_t *list; - int len = snd_hda_get_conn_list(codec, nid, &list); - - if (len <= 0) - return len; - if (len > max_conns) { - snd_printk(KERN_ERR "hda_codec: " - "Too many connections %d for NID 0x%x\n", - len, nid); - return -EINVAL; - } - memcpy(conn_list, list, len * sizeof(hda_nid_t)); - return len; -} -EXPORT_SYMBOL_HDA(snd_hda_get_connections); - -/** - * snd_hda_get_raw_connections - copy connection list without cache - * @codec: the HDA codec - * @nid: NID to parse - * @conn_list: connection list array - * @max_conns: max. number of connections to store - * - * Like snd_hda_get_connections(), copy the connection list but without - * checking through the connection-list cache. - * Currently called only from hda_proc.c, so not exported. - */ -int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t *conn_list, int max_conns) -{ - unsigned int parm; - int i, conn_len, conns; - unsigned int shift, num_elems, mask; - unsigned int wcaps; - hda_nid_t prev_nid; - - if (snd_BUG_ON(!conn_list || max_conns <= 0)) - return -EINVAL; - - wcaps = get_wcaps(codec, nid); - if (!(wcaps & AC_WCAP_CONN_LIST) && - get_wcaps_type(wcaps) != AC_WID_VOL_KNB) - return 0; - - parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN); - if (parm & AC_CLIST_LONG) { - /* long form */ - shift = 16; - num_elems = 2; - } else { - /* short form */ - shift = 8; - num_elems = 4; - } - conn_len = parm & AC_CLIST_LENGTH; - mask = (1 << (shift-1)) - 1; - - if (!conn_len) - return 0; /* no connection */ - - if (conn_len == 1) { - /* single connection */ - parm = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONNECT_LIST, 0); - if (parm == -1 && codec->bus->rirb_error) - return -EIO; - conn_list[0] = parm & mask; - return 1; - } - - /* multi connection */ - conns = 0; - prev_nid = 0; - for (i = 0; i < conn_len; i++) { - int range_val; - hda_nid_t val, n; - - if (i % num_elems == 0) { - parm = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONNECT_LIST, i); - if (parm == -1 && codec->bus->rirb_error) - return -EIO; - } - range_val = !!(parm & (1 << (shift-1))); /* ranges */ - val = parm & mask; - if (val == 0) { - snd_printk(KERN_WARNING "hda_codec: " - "invalid CONNECT_LIST verb %x[%i]:%x\n", - nid, i, parm); - return 0; - } - parm >>= shift; - if (range_val) { - /* ranges between the previous and this one */ - if (!prev_nid || prev_nid >= val) { - snd_printk(KERN_WARNING "hda_codec: " - "invalid dep_range_val %x:%x\n", - prev_nid, val); - continue; - } - for (n = prev_nid + 1; n <= val; n++) { - if (conns >= max_conns) { - snd_printk(KERN_ERR "hda_codec: " - "Too many connections %d for NID 0x%x\n", - conns, nid); - return -EINVAL; - } - conn_list[conns++] = n; - } - } else { - if (conns >= max_conns) { - snd_printk(KERN_ERR "hda_codec: " - "Too many connections %d for NID 0x%x\n", - conns, nid); - return -EINVAL; - } - conn_list[conns++] = val; - } - prev_nid = val; - } - return conns; -} - -static bool add_conn_list(struct snd_array *array, hda_nid_t nid) -{ - hda_nid_t *p = snd_array_new(array); - if (!p) - return false; - *p = nid; - return true; -} - -/** - * snd_hda_override_conn_list - add/modify the connection-list to cache - * @codec: the HDA codec - * @nid: NID to parse - * @len: number of connection list entries - * @list: the list of connection entries - * - * Add or modify the given connection-list to the cache. If the corresponding - * cache already exists, invalidate it and append a new one. - * - * Returns zero or a negative error code. - */ -int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int len, - const hda_nid_t *list) -{ - struct snd_array *array = &codec->conn_lists; - hda_nid_t *p; - int i, old_used; - - p = lookup_conn_list(array, nid); - if (p) - *p = -1; /* invalidate the old entry */ - - old_used = array->used; - if (!add_conn_list(array, nid) || !add_conn_list(array, len)) - goto error_add; - for (i = 0; i < len; i++) - if (!add_conn_list(array, list[i])) - goto error_add; - return 0; - - error_add: - array->used = old_used; - return -ENOMEM; -} -EXPORT_SYMBOL_HDA(snd_hda_override_conn_list); - -/** - * snd_hda_get_conn_index - get the connection index of the given NID - * @codec: the HDA codec - * @mux: NID containing the list - * @nid: NID to select - * @recursive: 1 when searching NID recursively, otherwise 0 - * - * Parses the connection list of the widget @mux and checks whether the - * widget @nid is present. If it is, return the connection index. - * Otherwise it returns -1. - */ -int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux, - hda_nid_t nid, int recursive) -{ - hda_nid_t conn[HDA_MAX_NUM_INPUTS]; - int i, nums; - - nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); - for (i = 0; i < nums; i++) - if (conn[i] == nid) - return i; - if (!recursive) - return -1; - if (recursive > 5) { - snd_printd("hda_codec: too deep connection for 0x%x\n", nid); - return -1; - } - recursive++; - for (i = 0; i < nums; i++) { - unsigned int type = get_wcaps_type(get_wcaps(codec, conn[i])); - if (type == AC_WID_PIN || type == AC_WID_AUD_OUT) - continue; - if (snd_hda_get_conn_index(codec, conn[i], nid, recursive) >= 0) - return i; - } - return -1; -} -EXPORT_SYMBOL_HDA(snd_hda_get_conn_index); - -/** - * snd_hda_queue_unsol_event - add an unsolicited event to queue - * @bus: the BUS - * @res: unsolicited event (lower 32bit of RIRB entry) - * @res_ex: codec addr and flags (upper 32bit or RIRB entry) - * - * Adds the given event to the queue. The events are processed in - * the workqueue asynchronously. Call this function in the interrupt - * hanlder when RIRB receives an unsolicited event. - * - * Returns 0 if successful, or a negative error code. - */ -int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex) -{ - struct hda_bus_unsolicited *unsol; - unsigned int wp; - - trace_hda_unsol_event(bus, res, res_ex); - unsol = bus->unsol; - if (!unsol) - return 0; - - wp = (unsol->wp + 1) % HDA_UNSOL_QUEUE_SIZE; - unsol->wp = wp; - - wp <<= 1; - unsol->queue[wp] = res; - unsol->queue[wp + 1] = res_ex; - - queue_work(bus->workq, &unsol->work); - - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_queue_unsol_event); - -/* - * process queued unsolicited events - */ -static void process_unsol_events(struct work_struct *work) -{ - struct hda_bus_unsolicited *unsol = - container_of(work, struct hda_bus_unsolicited, work); - struct hda_bus *bus = unsol->bus; - struct hda_codec *codec; - unsigned int rp, caddr, res; - - while (unsol->rp != unsol->wp) { - rp = (unsol->rp + 1) % HDA_UNSOL_QUEUE_SIZE; - unsol->rp = rp; - rp <<= 1; - res = unsol->queue[rp]; - caddr = unsol->queue[rp + 1]; - if (!(caddr & (1 << 4))) /* no unsolicited event? */ - continue; - codec = bus->caddr_tbl[caddr & 0x0f]; - if (codec && codec->patch_ops.unsol_event) - codec->patch_ops.unsol_event(codec, res); - } -} - -/* - * initialize unsolicited queue - */ -static int init_unsol_queue(struct hda_bus *bus) -{ - struct hda_bus_unsolicited *unsol; - - if (bus->unsol) /* already initialized */ - return 0; - - unsol = kzalloc(sizeof(*unsol), GFP_KERNEL); - if (!unsol) { - snd_printk(KERN_ERR "hda_codec: " - "can't allocate unsolicited queue\n"); - return -ENOMEM; - } - INIT_WORK(&unsol->work, process_unsol_events); - unsol->bus = bus; - bus->unsol = unsol; - return 0; -} - -/* - * destructor - */ -static void snd_hda_codec_free(struct hda_codec *codec); - -static int snd_hda_bus_free(struct hda_bus *bus) -{ - struct hda_codec *codec, *n; - - if (!bus) - return 0; - if (bus->workq) - flush_workqueue(bus->workq); - if (bus->unsol) - kfree(bus->unsol); - list_for_each_entry_safe(codec, n, &bus->codec_list, list) { - snd_hda_codec_free(codec); - } - if (bus->ops.private_free) - bus->ops.private_free(bus); - if (bus->workq) - destroy_workqueue(bus->workq); - kfree(bus); - return 0; -} - -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); - snd_hda_hwdep_add_power_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 - * @temp: the template for hda_bus information - * @busp: the pointer to store the created bus instance - * - * Returns 0 if successful, or a negative error code. - */ -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, - }; - - if (snd_BUG_ON(!temp)) - return -EINVAL; - if (snd_BUG_ON(!temp->ops.command || !temp->ops.get_response)) - return -EINVAL; - - if (busp) - *busp = NULL; - - bus = kzalloc(sizeof(*bus), GFP_KERNEL); - if (bus == NULL) { - snd_printk(KERN_ERR "can't allocate struct hda_bus\n"); - return -ENOMEM; - } - - bus->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); - mutex_init(&bus->prepare_mutex); - INIT_LIST_HEAD(&bus->codec_list); - - snprintf(bus->workq_name, sizeof(bus->workq_name), - "hd-audio%d", card->number); - bus->workq = create_singlethread_workqueue(bus->workq_name); - if (!bus->workq) { - snd_printk(KERN_ERR "cannot create workqueue %s\n", - bus->workq_name); - kfree(bus); - return -ENOMEM; - } - - err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops); - if (err < 0) { - snd_hda_bus_free(bus); - return err; - } - if (busp) - *busp = bus; - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_bus_new); - -#ifdef CONFIG_SND_HDA_GENERIC -#define is_generic_config(codec) \ - (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 * -find_codec_preset(struct hda_codec *codec) -{ - 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 */ - - 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; - if (preset->mfg && preset->mfg != codec->mfg) - continue; - if (!mask) - mask = ~0; - if (preset->id == (codec->vendor_id & mask) && - (!preset->rev || - 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; -} - -/* - * get_codec_name - store the codec name - */ -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]; - - if (codec->vendor_name) - goto get_chip_name; - - for (c = hda_vendor_ids; c->id; c++) { - if (c->id == vendor_id) { - vendor = c->name; - break; - } - } - if (!vendor) { - sprintf(tmp, "Generic %04x", vendor_id); - vendor = tmp; - } - codec->vendor_name = kstrdup(vendor, GFP_KERNEL); - if (!codec->vendor_name) - return -ENOMEM; - - get_chip_name: - if (codec->chip_name) - return 0; - - if (codec->preset && codec->preset->name) - codec->chip_name = kstrdup(codec->preset->name, GFP_KERNEL); - else { - sprintf(tmp, "ID %x", codec->vendor_id & 0xffff); - codec->chip_name = kstrdup(tmp, GFP_KERNEL); - } - if (!codec->chip_name) - return -ENOMEM; - return 0; -} - -/* - * look for an AFG and MFG nodes - */ -static void /*__devinit*/ setup_fg_nodes(struct hda_codec *codec) -{ - int i, total_nodes, function_id; - hda_nid_t nid; - - total_nodes = snd_hda_get_sub_nodes(codec, AC_NODE_ROOT, &nid); - for (i = 0; i < total_nodes; i++, nid++) { - function_id = snd_hda_param_read(codec, nid, - AC_PAR_FUNCTION_TYPE); - switch (function_id & 0xff) { - case AC_GRP_AUDIO_FUNCTION: - codec->afg = nid; - codec->afg_function_id = function_id & 0xff; - codec->afg_unsol = (function_id >> 8) & 1; - break; - case AC_GRP_MODEM_FUNCTION: - codec->mfg = nid; - codec->mfg_function_id = function_id & 0xff; - codec->mfg_unsol = (function_id >> 8) & 1; - break; - default: - break; - } - } -} - -/* - * read widget caps for each widget and store in cache - */ -static int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node) -{ - int i; - hda_nid_t nid; - - codec->num_nodes = snd_hda_get_sub_nodes(codec, fg_node, - &codec->start_nid); - codec->wcaps = kmalloc(codec->num_nodes * 4, GFP_KERNEL); - if (!codec->wcaps) - return -ENOMEM; - nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, nid++) - codec->wcaps[i] = snd_hda_param_read(codec, nid, - AC_PAR_AUDIO_WIDGET_CAP); - return 0; -} - -/* read all pin default configurations and save codec->init_pins */ -static int read_pin_defaults(struct hda_codec *codec) -{ - int i; - hda_nid_t nid = codec->start_nid; - - for (i = 0; i < codec->num_nodes; i++, nid++) { - struct hda_pincfg *pin; - unsigned int wcaps = get_wcaps(codec, nid); - unsigned int wid_type = get_wcaps_type(wcaps); - if (wid_type != AC_WID_PIN) - continue; - pin = snd_array_new(&codec->init_pins); - if (!pin) - return -ENOMEM; - pin->nid = nid; - pin->cfg = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONFIG_DEFAULT, 0); - pin->ctrl = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, - 0); - } - return 0; -} - -/* look up the given pin config list and return the item matching with NID */ -static struct hda_pincfg *look_up_pincfg(struct hda_codec *codec, - struct snd_array *array, - hda_nid_t nid) -{ - int i; - for (i = 0; i < array->used; i++) { - struct hda_pincfg *pin = snd_array_elem(array, i); - if (pin->nid == nid) - return pin; - } - return NULL; -} - -/* write a config value for the given NID */ -static void set_pincfg(struct hda_codec *codec, hda_nid_t nid, - unsigned int cfg) -{ - int i; - for (i = 0; i < 4; i++) { - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 + i, - cfg & 0xff); - cfg >>= 8; - } -} - -/* set the current pin config value for the given NID. - * the value is cached, and read via snd_hda_codec_get_pincfg() - */ -int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list, - hda_nid_t nid, unsigned int cfg) -{ - struct hda_pincfg *pin; - unsigned int oldcfg; - - if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) - return -EINVAL; - - oldcfg = snd_hda_codec_get_pincfg(codec, nid); - pin = look_up_pincfg(codec, list, nid); - if (!pin) { - pin = snd_array_new(list); - if (!pin) - return -ENOMEM; - pin->nid = nid; - } - pin->cfg = cfg; - - /* change only when needed; e.g. if the pincfg is already present - * in user_pins[], don't write it - */ - cfg = snd_hda_codec_get_pincfg(codec, nid); - if (oldcfg != cfg) - set_pincfg(codec, nid, cfg); - return 0; -} - -/** - * snd_hda_codec_set_pincfg - Override a pin default configuration - * @codec: the HDA codec - * @nid: NID to set the pin config - * @cfg: the pin default config value - * - * Override a pin default configuration value in the cache. - * This value can be read by snd_hda_codec_get_pincfg() in a higher - * priority than the real hardware value. - */ -int snd_hda_codec_set_pincfg(struct hda_codec *codec, - hda_nid_t nid, unsigned int cfg) -{ - return snd_hda_add_pincfg(codec, &codec->driver_pins, nid, cfg); -} -EXPORT_SYMBOL_HDA(snd_hda_codec_set_pincfg); - -/** - * snd_hda_codec_get_pincfg - Obtain a pin-default configuration - * @codec: the HDA codec - * @nid: NID to get the pin config - * - * Get the current pin config value of the given pin NID. - * If the pincfg value is cached or overridden via sysfs or driver, - * returns the cached value. - */ -unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid) -{ - struct hda_pincfg *pin; - -#ifdef CONFIG_SND_HDA_HWDEP - pin = look_up_pincfg(codec, &codec->user_pins, nid); - if (pin) - return pin->cfg; -#endif - pin = look_up_pincfg(codec, &codec->driver_pins, nid); - if (pin) - return pin->cfg; - pin = look_up_pincfg(codec, &codec->init_pins, nid); - if (pin) - return pin->cfg; - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_codec_get_pincfg); - -/* restore all current pin configs */ -static void restore_pincfgs(struct hda_codec *codec) -{ - int i; - for (i = 0; i < codec->init_pins.used; i++) { - struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); - set_pincfg(codec, pin->nid, - snd_hda_codec_get_pincfg(codec, pin->nid)); - } -} - -/** - * snd_hda_shutup_pins - Shut up all pins - * @codec: the HDA codec - * - * Clear all pin controls to shup up before suspend for avoiding click noise. - * The controls aren't cached so that they can be resumed properly. - */ -void snd_hda_shutup_pins(struct hda_codec *codec) -{ - int i; - /* don't shut up pins when unloading the driver; otherwise it breaks - * the default pin setup at the next load of the driver - */ - if (codec->bus->shutdown) - return; - for (i = 0; i < codec->init_pins.used; i++) { - struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); - /* use read here for syncing after issuing each verb */ - snd_hda_codec_read(codec, pin->nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0); - } - codec->pins_shutup = 1; -} -EXPORT_SYMBOL_HDA(snd_hda_shutup_pins); - -#ifdef CONFIG_PM -/* Restore the pin controls cleared previously via snd_hda_shutup_pins() */ -static void restore_shutup_pins(struct hda_codec *codec) -{ - int i; - if (!codec->pins_shutup) - return; - if (codec->bus->shutdown) - return; - for (i = 0; i < codec->init_pins.used; i++) { - struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); - snd_hda_codec_write(codec, pin->nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - pin->ctrl); - } - codec->pins_shutup = 0; -} -#endif - -static void init_hda_cache(struct hda_cache_rec *cache, - unsigned int record_size); -static void free_hda_cache(struct hda_cache_rec *cache); - -/* restore the initial pin cfgs and release all pincfg lists */ -static void restore_init_pincfgs(struct hda_codec *codec) -{ - /* first free driver_pins and user_pins, then call restore_pincfg - * so that only the values in init_pins are restored - */ - snd_array_free(&codec->driver_pins); -#ifdef CONFIG_SND_HDA_HWDEP - snd_array_free(&codec->user_pins); -#endif - restore_pincfgs(codec); - snd_array_free(&codec->init_pins); -} - -/* - * audio-converter setup caches - */ -struct hda_cvt_setup { - hda_nid_t nid; - u8 stream_tag; - u8 channel_id; - u16 format_id; - unsigned char active; /* cvt is currently used */ - unsigned char dirty; /* setups should be cleared */ -}; - -/* get or create a cache entry for the given audio converter NID */ -static struct hda_cvt_setup * -get_hda_cvt_setup(struct hda_codec *codec, hda_nid_t nid) -{ - struct hda_cvt_setup *p; - int i; - - for (i = 0; i < codec->cvt_setups.used; i++) { - p = snd_array_elem(&codec->cvt_setups, i); - if (p->nid == nid) - return p; - } - p = snd_array_new(&codec->cvt_setups); - if (p) - p->nid = nid; - return p; -} - -/* - * codec destructor - */ -static void snd_hda_codec_free(struct hda_codec *codec) -{ - if (!codec) - return; - snd_hda_jack_tbl_clear(codec); - restore_init_pincfgs(codec); -#ifdef CONFIG_SND_HDA_POWER_SAVE - cancel_delayed_work(&codec->power_work); - flush_workqueue(codec->bus->workq); -#endif - list_del(&codec->list); - snd_array_free(&codec->mixers); - snd_array_free(&codec->nids); - snd_array_free(&codec->cvt_setups); - snd_array_free(&codec->conn_lists); - snd_array_free(&codec->spdif_out); - 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->vendor_name); - kfree(codec->chip_name); - kfree(codec->modelname); - kfree(codec->wcaps); - kfree(codec); -} - -static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, - unsigned int power_state); - -/** - * snd_hda_codec_new - create a HDA codec - * @bus: the bus to assign - * @codec_addr: the codec address - * @codecp: the pointer to store the generated 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) -{ - struct hda_codec *codec; - char component[31]; - int err; - - if (snd_BUG_ON(!bus)) - return -EINVAL; - if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS)) - return -EINVAL; - - if (bus->caddr_tbl[codec_addr]) { - snd_printk(KERN_ERR "hda_codec: " - "address 0x%x is already occupied\n", codec_addr); - return -EBUSY; - } - - codec = kzalloc(sizeof(*codec), GFP_KERNEL); - if (codec == NULL) { - snd_printk(KERN_ERR "can't allocate struct hda_codec\n"); - return -ENOMEM; - } - - codec->bus = bus; - codec->addr = codec_addr; - mutex_init(&codec->spdif_mutex); - mutex_init(&codec->control_mutex); - init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info)); - init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head)); - snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32); - snd_array_init(&codec->nids, sizeof(struct hda_nid_item), 32); - snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16); - snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16); - snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8); - snd_array_init(&codec->conn_lists, sizeof(hda_nid_t), 64); - snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16); - 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); - /* snd_hda_codec_new() marks the codec as power-up, and leave it as is. - * the caller has to power down appropriatley after initialization - * phase. - */ - hda_keep_power_on(codec); -#endif - - list_add_tail(&codec->list, &bus->codec_list); - bus->caddr_tbl[codec_addr] = codec; - - codec->vendor_id = snd_hda_param_read(codec, AC_NODE_ROOT, - AC_PAR_VENDOR_ID); - if (codec->vendor_id == -1) - /* read again, hopefully the access method was corrected - * in the last read... - */ - codec->vendor_id = snd_hda_param_read(codec, AC_NODE_ROOT, - AC_PAR_VENDOR_ID); - codec->subsystem_id = snd_hda_param_read(codec, AC_NODE_ROOT, - AC_PAR_SUBSYSTEM_ID); - codec->revision_id = snd_hda_param_read(codec, AC_NODE_ROOT, - AC_PAR_REV_ID); - - setup_fg_nodes(codec); - if (!codec->afg && !codec->mfg) { - snd_printdd("hda_codec: no AFG or MFG node found\n"); - err = -ENODEV; - goto error; - } - - err = read_widget_caps(codec, codec->afg ? codec->afg : codec->mfg); - if (err < 0) { - snd_printk(KERN_ERR "hda_codec: cannot malloc\n"); - goto error; - } - err = read_pin_defaults(codec); - if (err < 0) - goto error; - - if (!codec->subsystem_id) { - hda_nid_t nid = codec->afg ? codec->afg : codec->mfg; - codec->subsystem_id = - snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_SUBSYSTEM_ID, 0); - } - - /* power-up all before initialization */ - hda_set_power_state(codec, - codec->afg ? codec->afg : codec->mfg, - AC_PWRST_D0); - - 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; - - error: - snd_hda_codec_free(codec); - return err; -} -EXPORT_SYMBOL_HDA(snd_hda_codec_new); - -/** - * snd_hda_codec_configure - (Re-)configure the HD-audio codec - * @codec: the HDA codec - * - * Start parsing of the given codec tree and (re-)initialize the whole - * patch instance. - * - * Returns 0 if successful or a negative error code. - */ -int snd_hda_codec_configure(struct hda_codec *codec) -{ - int err; - - codec->preset = find_codec_preset(codec); - if (!codec->vendor_name || !codec->chip_name) { - err = get_codec_name(codec); - if (err < 0) - return err; - } - - if (is_generic_config(codec)) { - err = snd_hda_parse_generic_codec(codec); - goto patched; - } - if (codec->preset && codec->preset->patch) { - err = codec->preset->patch(codec); - goto patched; - } - - /* call the default parser */ - err = snd_hda_parse_generic_codec(codec); - if (err < 0) - printk(KERN_ERR "hda-codec: No codec parser is available\n"); - - patched: - if (!err && codec->patch_ops.unsol_event) - err = init_unsol_queue(codec->bus); - /* audio codec should override the mixer name */ - if (!err && (codec->afg || !*codec->bus->card->mixername)) - snprintf(codec->bus->card->mixername, - sizeof(codec->bus->card->mixername), - "%s %s", codec->vendor_name, codec->chip_name); - return err; -} -EXPORT_SYMBOL_HDA(snd_hda_codec_configure); - -/** - * snd_hda_codec_setup_stream - set up the codec for streaming - * @codec: the CODEC to set up - * @nid: the NID to set up - * @stream_tag: stream tag to pass, it's between 0x1 and 0xf. - * @channel_id: channel id to pass, zero based. - * @format: stream format. - */ -void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, - u32 stream_tag, - int channel_id, int format) -{ - struct hda_codec *c; - struct hda_cvt_setup *p; - unsigned int oldval, newval; - int type; - int i; - - if (!nid) - return; - - snd_printdd("hda_codec_setup_stream: " - "NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n", - nid, stream_tag, channel_id, format); - p = get_hda_cvt_setup(codec, nid); - if (!p) - return; - /* update the stream-id if changed */ - if (p->stream_tag != stream_tag || p->channel_id != channel_id) { - oldval = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); - newval = (stream_tag << 4) | channel_id; - if (oldval != newval) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_CHANNEL_STREAMID, - newval); - p->stream_tag = stream_tag; - p->channel_id = channel_id; - } - /* update the format-id if changed */ - if (p->format_id != format) { - oldval = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_STREAM_FORMAT, 0); - if (oldval != format) { - msleep(1); - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_STREAM_FORMAT, - format); - } - p->format_id = format; - } - p->active = 1; - p->dirty = 0; - - /* make other inactive cvts with the same stream-tag dirty */ - type = get_wcaps_type(get_wcaps(codec, nid)); - list_for_each_entry(c, &codec->bus->codec_list, list) { - for (i = 0; i < c->cvt_setups.used; i++) { - p = snd_array_elem(&c->cvt_setups, i); - if (!p->active && p->stream_tag == stream_tag && - get_wcaps_type(get_wcaps(c, p->nid)) == type) - p->dirty = 1; - } - } -} -EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream); - -static void really_cleanup_stream(struct hda_codec *codec, - struct hda_cvt_setup *q); - -/** - * __snd_hda_codec_cleanup_stream - clean up the codec for closing - * @codec: the CODEC to clean up - * @nid: the NID to clean up - * @do_now: really clean up the stream instead of clearing the active flag - */ -void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid, - int do_now) -{ - struct hda_cvt_setup *p; - - if (!nid) - return; - - if (codec->no_sticky_stream) - do_now = 1; - - snd_printdd("hda_codec_cleanup_stream: NID=0x%x\n", nid); - p = get_hda_cvt_setup(codec, nid); - if (p) { - /* here we just clear the active flag when do_now isn't set; - * actual clean-ups will be done later in - * purify_inactive_streams() called from snd_hda_codec_prpapre() - */ - if (do_now) - really_cleanup_stream(codec, p); - else - p->active = 0; - } -} -EXPORT_SYMBOL_HDA(__snd_hda_codec_cleanup_stream); - -static void really_cleanup_stream(struct hda_codec *codec, - struct hda_cvt_setup *q) -{ - hda_nid_t nid = q->nid; - if (q->stream_tag || q->channel_id) - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0); - if (q->format_id) - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0 -); - memset(q, 0, sizeof(*q)); - q->nid = nid; -} - -/* clean up the all conflicting obsolete streams */ -static void purify_inactive_streams(struct hda_codec *codec) -{ - struct hda_codec *c; - int i; - - list_for_each_entry(c, &codec->bus->codec_list, list) { - for (i = 0; i < c->cvt_setups.used; i++) { - struct hda_cvt_setup *p; - p = snd_array_elem(&c->cvt_setups, i); - if (p->dirty) - really_cleanup_stream(c, p); - } - } -} - -#ifdef CONFIG_PM -/* clean up all streams; called from suspend */ -static void hda_cleanup_all_streams(struct hda_codec *codec) -{ - int i; - - for (i = 0; i < codec->cvt_setups.used; i++) { - struct hda_cvt_setup *p = snd_array_elem(&codec->cvt_setups, i); - if (p->stream_tag) - really_cleanup_stream(codec, p); - } -} -#endif - -/* - * amp access functions - */ - -/* FIXME: more better hash key? */ -#define HDA_HASH_KEY(nid, dir, idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24)) -#define HDA_HASH_PINCAP_KEY(nid) (u32)((nid) + (0x02 << 24)) -#define HDA_HASH_PARPCM_KEY(nid) (u32)((nid) + (0x03 << 24)) -#define HDA_HASH_PARSTR_KEY(nid) (u32)((nid) + (0x04 << 24)) -#define INFO_AMP_CAPS (1<<0) -#define INFO_AMP_VOL(ch) (1 << (1 + (ch))) - -/* initialize the hash table */ -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)); - snd_array_init(&cache->buf, record_size, 64); -} - -static void free_hda_cache(struct hda_cache_rec *cache) -{ - snd_array_free(&cache->buf); -} - -/* query the hash. allocate an entry if not found. */ -static struct hda_cache_head *get_hash(struct hda_cache_rec *cache, u32 key) -{ - u16 idx = key % (u16)ARRAY_SIZE(cache->hash); - u16 cur = cache->hash[idx]; - struct hda_cache_head *info; - - while (cur != 0xffff) { - info = snd_array_elem(&cache->buf, cur); - if (info->key == key) - return info; - cur = info->next; - } - return NULL; -} - -/* query the hash. allocate an entry if not found. */ -static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache, - u32 key) -{ - struct hda_cache_head *info = get_hash(cache, key); - if (!info) { - u16 idx, cur; - /* add a new hash entry */ - info = snd_array_new(&cache->buf); - if (!info) - return NULL; - cur = snd_array_index(&cache->buf, info); - info->key = key; - info->val = 0; - idx = key % (u16)ARRAY_SIZE(cache->hash); - info->next = cache->hash[idx]; - cache->hash[idx] = cur; - } - return info; -} - -/* query and allocate an amp hash entry */ -static inline struct hda_amp_info * -get_alloc_amp_hash(struct hda_codec *codec, u32 key) -{ - return (struct hda_amp_info *)get_alloc_hash(&codec->amp_cache, key); -} - -/** - * query_amp_caps - query AMP capabilities - * @codec: the HD-auio codec - * @nid: the NID to query - * @direction: either #HDA_INPUT or #HDA_OUTPUT - * - * Query AMP capabilities for the given widget and direction. - * Returns the obtained capability bits. - * - * When cap bits have been already read, this doesn't read again but - * returns the cached value. - */ -u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction) -{ - struct hda_amp_info *info; - - info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, 0)); - if (!info) - return 0; - if (!(info->head.val & INFO_AMP_CAPS)) { - if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD)) - nid = codec->afg; - info->amp_caps = snd_hda_param_read(codec, nid, - direction == HDA_OUTPUT ? - AC_PAR_AMP_OUT_CAP : - AC_PAR_AMP_IN_CAP); - if (info->amp_caps) - info->head.val |= INFO_AMP_CAPS; - } - return info->amp_caps; -} -EXPORT_SYMBOL_HDA(query_amp_caps); - -/** - * snd_hda_override_amp_caps - Override the AMP capabilities - * @codec: the CODEC to clean up - * @nid: the NID to clean up - * @direction: either #HDA_INPUT or #HDA_OUTPUT - * @caps: the capability bits to set - * - * Override the cached AMP caps bits value by the given one. - * This function is useful if the driver needs to adjust the AMP ranges, - * e.g. limit to 0dB, etc. - * - * Returns zero if successful or a negative error code. - */ -int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, - unsigned int caps) -{ - struct hda_amp_info *info; - - info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, dir, 0)); - if (!info) - return -EINVAL; - info->amp_caps = caps; - info->head.val |= INFO_AMP_CAPS; - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_override_amp_caps); - -static unsigned int -query_caps_hash(struct hda_codec *codec, hda_nid_t nid, u32 key, - unsigned int (*func)(struct hda_codec *, hda_nid_t)) -{ - struct hda_amp_info *info; - - info = get_alloc_amp_hash(codec, key); - if (!info) - return 0; - if (!info->head.val) { - info->head.val |= INFO_AMP_CAPS; - info->amp_caps = func(codec, nid); - } - return info->amp_caps; -} - -static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid) -{ - return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); -} - -/** - * snd_hda_query_pin_caps - Query PIN capabilities - * @codec: the HD-auio codec - * @nid: the NID to query - * - * Query PIN capabilities for the given widget. - * Returns the obtained capability bits. - * - * When cap bits have been already read, this doesn't read again but - * returns the cached value. - */ -u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid) -{ - return query_caps_hash(codec, nid, HDA_HASH_PINCAP_KEY(nid), - read_pin_cap); -} -EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps); - -/** - * snd_hda_override_pin_caps - Override the pin capabilities - * @codec: the CODEC - * @nid: the NID to override - * @caps: the capability bits to set - * - * Override the cached PIN capabilitiy bits value by the given one. - * - * Returns zero if successful or a negative error code. - */ -int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid, - unsigned int caps) -{ - struct hda_amp_info *info; - info = get_alloc_amp_hash(codec, HDA_HASH_PINCAP_KEY(nid)); - if (!info) - return -ENOMEM; - info->amp_caps = caps; - info->head.val |= INFO_AMP_CAPS; - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_override_pin_caps); - -/* - * read the current volume to info - * if the cache exists, read the cache value. - */ -static unsigned int get_vol_mute(struct hda_codec *codec, - struct hda_amp_info *info, hda_nid_t nid, - int ch, int direction, int index) -{ - u32 val, parm; - - if (info->head.val & INFO_AMP_VOL(ch)) - return info->vol[ch]; - - parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT; - parm |= direction == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT; - parm |= index; - val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_AMP_GAIN_MUTE, parm); - info->vol[ch] = val & 0xff; - info->head.val |= INFO_AMP_VOL(ch); - return info->vol[ch]; -} - -/* - * write the current volume in info to the h/w and update the cache - */ -static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info, - hda_nid_t nid, int ch, int direction, int index, - int val) -{ - u32 parm; - - parm = ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT; - parm |= direction == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT; - parm |= index << AC_AMP_SET_INDEX_SHIFT; - if ((val & HDA_AMP_MUTE) && !(info->amp_caps & AC_AMPCAP_MUTE) && - (info->amp_caps & AC_AMPCAP_MIN_MUTE)) - ; /* set the zero value as a fake mute */ - else - parm |= val; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, parm); - info->vol[ch] = val; -} - -/** - * snd_hda_codec_amp_read - Read AMP value - * @codec: HD-audio codec - * @nid: NID to read the AMP value - * @ch: channel (left=0 or right=1) - * @direction: #HDA_INPUT or #HDA_OUTPUT - * @index: the index value (only for input direction) - * - * Read AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit. - */ -int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, - int direction, int index) -{ - struct hda_amp_info *info; - info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index)); - if (!info) - return 0; - return get_vol_mute(codec, info, nid, ch, direction, index); -} -EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read); - -/** - * snd_hda_codec_amp_update - update the AMP value - * @codec: HD-audio codec - * @nid: NID to read the AMP value - * @ch: channel (left=0 or right=1) - * @direction: #HDA_INPUT or #HDA_OUTPUT - * @idx: the index value (only for input direction) - * @mask: bit mask to set - * @val: the bits value to set - * - * Update the AMP value with a bit mask. - * Returns 0 if the value is unchanged, 1 if changed. - */ -int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, - int direction, int idx, int mask, int val) -{ - struct hda_amp_info *info; - - info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, idx)); - if (!info) - return 0; - if (snd_BUG_ON(mask & ~0xff)) - mask &= 0xff; - val &= mask; - val |= get_vol_mute(codec, info, nid, ch, direction, idx) & ~mask; - if (info->vol[ch] == val) - return 0; - put_vol_mute(codec, info, nid, ch, direction, idx, val); - return 1; -} -EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update); - -/** - * snd_hda_codec_amp_stereo - update the AMP stereo values - * @codec: HD-audio codec - * @nid: NID to read the AMP value - * @direction: #HDA_INPUT or #HDA_OUTPUT - * @idx: the index value (only for input direction) - * @mask: bit mask to set - * @val: the bits value to set - * - * Update the AMP values like snd_hda_codec_amp_update(), but for a - * stereo widget with the same mask and value. - */ -int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, - int direction, int idx, int mask, int val) -{ - int ch, ret = 0; - - if (snd_BUG_ON(mask & ~0xff)) - mask &= 0xff; - for (ch = 0; ch < 2; ch++) - ret |= snd_hda_codec_amp_update(codec, nid, ch, direction, - idx, mask, val); - return ret; -} -EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo); - -#ifdef CONFIG_PM -/** - * snd_hda_codec_resume_amp - Resume all AMP commands from the cache - * @codec: HD-audio codec - * - * Resume the all amp commands from the cache. - */ -void snd_hda_codec_resume_amp(struct hda_codec *codec) -{ - struct hda_amp_info *buffer = codec->amp_cache.buf.list; - int i; - - 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; - if (!key) - continue; - nid = key & 0xff; - idx = (key >> 16) & 0xff; - dir = (key >> 24) & 0xff; - for (ch = 0; ch < 2; ch++) { - if (!(buffer->head.val & INFO_AMP_VOL(ch))) - continue; - put_vol_mute(codec, buffer, nid, ch, dir, idx, - buffer->vol[ch]); - } - } -} -EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp); -#endif /* CONFIG_PM */ - -static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir, - unsigned int ofs) -{ - u32 caps = query_amp_caps(codec, nid, dir); - /* get num steps */ - caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; - if (ofs < caps) - caps -= ofs; - return caps; -} - -/** - * snd_hda_mixer_amp_volume_info - Info callback for a standard AMP mixer - * - * The control element is supposed to have the private_value field - * set up via HDA_COMPOSE_AMP_VAL*() or related macros. - */ -int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - u16 nid = get_amp_nid(kcontrol); - u8 chs = get_amp_channels(kcontrol); - int dir = get_amp_direction(kcontrol); - unsigned int ofs = get_amp_offset(kcontrol); - - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = chs == 3 ? 2 : 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = get_amp_max_value(codec, nid, dir, ofs); - if (!uinfo->value.integer.max) { - printk(KERN_WARNING "hda_codec: " - "num_steps = 0 for NID=0x%x (ctl = %s)\n", nid, - kcontrol->id.name); - return -EINVAL; - } - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_info); - - -static inline unsigned int -read_amp_value(struct hda_codec *codec, hda_nid_t nid, - int ch, int dir, int idx, unsigned int ofs) -{ - unsigned int val; - val = snd_hda_codec_amp_read(codec, nid, ch, dir, idx); - val &= HDA_AMP_VOLMASK; - if (val >= ofs) - val -= ofs; - else - val = 0; - return val; -} - -static inline int -update_amp_value(struct hda_codec *codec, hda_nid_t nid, - int ch, int dir, int idx, unsigned int ofs, - unsigned int val) -{ - unsigned int maxval; - - if (val > 0) - val += ofs; - /* ofs = 0: raw max value */ - maxval = get_amp_max_value(codec, nid, dir, 0); - if (val > maxval) - val = maxval; - return snd_hda_codec_amp_update(codec, nid, ch, dir, idx, - HDA_AMP_VOLMASK, val); -} - -/** - * snd_hda_mixer_amp_volume_get - Get callback for a standard AMP mixer volume - * - * The control element is supposed to have the private_value field - * set up via HDA_COMPOSE_AMP_VAL*() or related macros. - */ -int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = get_amp_nid(kcontrol); - int chs = get_amp_channels(kcontrol); - int dir = get_amp_direction(kcontrol); - int idx = get_amp_index(kcontrol); - unsigned int ofs = get_amp_offset(kcontrol); - long *valp = ucontrol->value.integer.value; - - if (chs & 1) - *valp++ = read_amp_value(codec, nid, 0, dir, idx, ofs); - if (chs & 2) - *valp = read_amp_value(codec, nid, 1, dir, idx, ofs); - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_get); - -/** - * snd_hda_mixer_amp_volume_put - Put callback for a standard AMP mixer volume - * - * The control element is supposed to have the private_value field - * set up via HDA_COMPOSE_AMP_VAL*() or related macros. - */ -int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = get_amp_nid(kcontrol); - int chs = get_amp_channels(kcontrol); - int dir = get_amp_direction(kcontrol); - int idx = get_amp_index(kcontrol); - unsigned int ofs = get_amp_offset(kcontrol); - long *valp = ucontrol->value.integer.value; - int change = 0; - - snd_hda_power_up(codec); - if (chs & 1) { - change = update_amp_value(codec, nid, 0, dir, idx, ofs, *valp); - valp++; - } - if (chs & 2) - change |= update_amp_value(codec, nid, 1, dir, idx, ofs, *valp); - snd_hda_power_down(codec); - return change; -} -EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_put); - -/** - * snd_hda_mixer_amp_volume_put - TLV callback for a standard AMP mixer volume - * - * The control element is supposed to have the private_value field - * set up via HDA_COMPOSE_AMP_VAL*() or related macros. - */ -int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, - unsigned int size, unsigned int __user *_tlv) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = get_amp_nid(kcontrol); - int dir = get_amp_direction(kcontrol); - unsigned int ofs = get_amp_offset(kcontrol); - bool min_mute = get_amp_min_mute(kcontrol); - u32 caps, val1, val2; - - if (size < 4 * sizeof(unsigned int)) - return -ENOMEM; - caps = query_amp_caps(codec, nid, dir); - val2 = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT; - val2 = (val2 + 1) * 25; - val1 = -((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT); - val1 += ofs; - val1 = ((int)val1) * ((int)val2); - if (min_mute || (caps & AC_AMPCAP_MIN_MUTE)) - val2 |= TLV_DB_SCALE_MUTE; - if (put_user(SNDRV_CTL_TLVT_DB_SCALE, _tlv)) - return -EFAULT; - if (put_user(2 * sizeof(unsigned int), _tlv + 1)) - return -EFAULT; - if (put_user(val1, _tlv + 2)) - return -EFAULT; - if (put_user(val2, _tlv + 3)) - return -EFAULT; - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_tlv); - -/** - * snd_hda_set_vmaster_tlv - Set TLV for a virtual master control - * @codec: HD-audio codec - * @nid: NID of a reference widget - * @dir: #HDA_INPUT or #HDA_OUTPUT - * @tlv: TLV data to be stored, at least 4 elements - * - * Set (static) TLV data for a virtual master volume using the AMP caps - * obtained from the reference NID. - * The volume range is recalculated as if the max volume is 0dB. - */ -void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir, - unsigned int *tlv) -{ - u32 caps; - int nums, step; - - caps = query_amp_caps(codec, nid, dir); - nums = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; - step = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT; - step = (step + 1) * 25; - tlv[0] = SNDRV_CTL_TLVT_DB_SCALE; - tlv[1] = 2 * sizeof(unsigned int); - 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 * -_snd_hda_find_mixer_ctl(struct hda_codec *codec, - const char *name, int idx) -{ - struct snd_ctl_elem_id id; - memset(&id, 0, sizeof(id)); - id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - id.index = idx; - if (snd_BUG_ON(strlen(name) >= sizeof(id.name))) - return NULL; - strcpy(id.name, name); - return snd_ctl_find_id(codec->bus->card, &id); -} - -/** - * snd_hda_find_mixer_ctl - Find a mixer control element with the given name - * @codec: HD-audio codec - * @name: ctl id name string - * - * Get the control element with the given id string and IFACE_MIXER. - */ -struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, - const char *name) -{ - return _snd_hda_find_mixer_ctl(codec, name, 0); -} -EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl); - -static int find_empty_mixer_ctl_idx(struct hda_codec *codec, const char *name) -{ - int idx; - for (idx = 0; idx < 16; idx++) { /* 16 ctlrs should be large enough */ - if (!_snd_hda_find_mixer_ctl(codec, name, idx)) - return idx; - } - return -EBUSY; -} - -/** - * snd_hda_ctl_add - Add a control element and assign to the codec - * @codec: HD-audio codec - * @nid: corresponding NID (optional) - * @kctl: the control element to assign - * - * Add the given control element to an array inside the codec instance. - * All control elements belonging to a codec are supposed to be added - * by this function so that a proper clean-up works at the free or - * reconfiguration time. - * - * If non-zero @nid is passed, the NID is assigned to the control element. - * The assignment is shown in the codec proc file. - * - * snd_hda_ctl_add() checks the control subdev id field whether - * #HDA_SUBDEV_NID_FLAG bit is set. If set (and @nid is zero), the lower - * bits value is taken as the NID to assign. The #HDA_NID_ITEM_AMP bit - * specifies if kctl->private_value is a HDA amplifier value. - */ -int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, - struct snd_kcontrol *kctl) -{ - int err; - unsigned short flags = 0; - struct hda_nid_item *item; - - if (kctl->id.subdevice & HDA_SUBDEV_AMP_FLAG) { - flags |= HDA_NID_ITEM_AMP; - if (nid == 0) - nid = get_amp_nid_(kctl->private_value); - } - if ((kctl->id.subdevice & HDA_SUBDEV_NID_FLAG) != 0 && nid == 0) - nid = kctl->id.subdevice & 0xffff; - if (kctl->id.subdevice & (HDA_SUBDEV_NID_FLAG|HDA_SUBDEV_AMP_FLAG)) - kctl->id.subdevice = 0; - err = snd_ctl_add(codec->bus->card, kctl); - if (err < 0) - return err; - item = snd_array_new(&codec->mixers); - if (!item) - return -ENOMEM; - item->kctl = kctl; - item->nid = nid; - item->flags = flags; - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_ctl_add); - -/** - * snd_hda_add_nid - Assign a NID to a control element - * @codec: HD-audio codec - * @nid: corresponding NID (optional) - * @kctl: the control element to assign - * @index: index to kctl - * - * Add the given control element to an array inside the codec instance. - * This function is used when #snd_hda_ctl_add cannot be used for 1:1 - * NID:KCTL mapping - for example "Capture Source" selector. - */ -int snd_hda_add_nid(struct hda_codec *codec, struct snd_kcontrol *kctl, - unsigned int index, hda_nid_t nid) -{ - struct hda_nid_item *item; - - if (nid > 0) { - item = snd_array_new(&codec->nids); - if (!item) - return -ENOMEM; - item->kctl = kctl; - item->index = index; - item->nid = nid; - return 0; - } - printk(KERN_ERR "hda-codec: no NID for mapping control %s:%d:%d\n", - kctl->id.name, kctl->id.index, index); - return -EINVAL; -} -EXPORT_SYMBOL_HDA(snd_hda_add_nid); - -/** - * snd_hda_ctls_clear - Clear all controls assigned to the given codec - * @codec: HD-audio codec - */ -void snd_hda_ctls_clear(struct hda_codec *codec) -{ - int i; - struct hda_nid_item *items = codec->mixers.list; - for (i = 0; i < codec->mixers.used; i++) - snd_ctl_remove(codec->bus->card, items[i].kctl); - snd_array_free(&codec->mixers); - snd_array_free(&codec->nids); -} - -/* pseudo device locking - * toggle card->shutdown to allow/disallow the device access (as a hack) - */ -static int hda_lock_devices(struct snd_card *card) -{ - spin_lock(&card->files_lock); - if (card->shutdown) { - spin_unlock(&card->files_lock); - return -EINVAL; - } - card->shutdown = 1; - spin_unlock(&card->files_lock); - return 0; -} - -static void hda_unlock_devices(struct snd_card *card) -{ - spin_lock(&card->files_lock); - card->shutdown = 0; - spin_unlock(&card->files_lock); -} - -/** - * snd_hda_codec_reset - Clear all objects assigned to the codec - * @codec: HD-audio codec - * - * This frees the all PCM and control elements assigned to the codec, and - * clears the caches and restores the pin default configurations. - * - * When a device is being used, it returns -EBSY. If successfully freed, - * returns zero. - */ -int snd_hda_codec_reset(struct hda_codec *codec) -{ - struct snd_card *card = codec->bus->card; - int i, pcm; - - if (hda_lock_devices(card) < 0) - return -EBUSY; - /* check whether the codec isn't used by any mixer or PCM streams */ - if (!list_empty(&card->ctl_files)) { - hda_unlock_devices(card); - return -EBUSY; - } - for (pcm = 0; pcm < codec->num_pcms; pcm++) { - struct hda_pcm *cpcm = &codec->pcm_info[pcm]; - if (!cpcm->pcm) - continue; - if (cpcm->pcm->streams[0].substream_opened || - cpcm->pcm->streams[1].substream_opened) { - hda_unlock_devices(card); - return -EBUSY; - } - } - - /* OK, let it free */ - -#ifdef CONFIG_SND_HDA_POWER_SAVE - cancel_delayed_work(&codec->power_work); - flush_workqueue(codec->bus->workq); -#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(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); - snd_hda_jack_tbl_clear(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)); - /* free only driver_pins so that init_pins + user_pins are restored */ - snd_array_free(&codec->driver_pins); - restore_pincfgs(codec); - codec->num_pcms = 0; - codec->pcm_info = NULL; - codec->preset = NULL; - memset(&codec->patch_ops, 0, sizeof(codec->patch_ops)); - codec->slave_dig_outs = NULL; - codec->spdif_status_reset = 0; - module_put(codec->owner); - codec->owner = NULL; - - /* allow device access again */ - hda_unlock_devices(card); - return 0; -} - -typedef int (*map_slave_func_t)(void *, struct snd_kcontrol *); - -/* apply the function to all matching slave ctls in the mixer list */ -static int map_slaves(struct hda_codec *codec, const char * const *slaves, - const char *suffix, map_slave_func_t func, void *data) -{ - struct hda_nid_item *items; - const char * const *s; - int i, err; - - items = codec->mixers.list; - for (i = 0; i < codec->mixers.used; i++) { - struct snd_kcontrol *sctl = items[i].kctl; - if (!sctl || !sctl->id.name || - sctl->id.iface != SNDRV_CTL_ELEM_IFACE_MIXER) - continue; - for (s = slaves; *s; s++) { - char tmpname[sizeof(sctl->id.name)]; - const char *name = *s; - if (suffix) { - snprintf(tmpname, sizeof(tmpname), "%s %s", - name, suffix); - name = tmpname; - } - if (!strcmp(sctl->id.name, name)) { - err = func(data, sctl); - if (err) - return err; - break; - } - } - } - return 0; -} - -static int check_slave_present(void *data, struct snd_kcontrol *sctl) -{ - return 1; -} - -/* guess the value corresponding to 0dB */ -static int get_kctl_0dB_offset(struct snd_kcontrol *kctl) -{ - int _tlv[4]; - const int *tlv = NULL; - int val = -1; - - if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { - /* FIXME: set_fs() hack for obtaining user-space TLV data */ - mm_segment_t fs = get_fs(); - set_fs(get_ds()); - if (!kctl->tlv.c(kctl, 0, sizeof(_tlv), _tlv)) - tlv = _tlv; - set_fs(fs); - } else if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) - tlv = kctl->tlv.p; - if (tlv && tlv[0] == SNDRV_CTL_TLVT_DB_SCALE) - val = -tlv[2] / tlv[3]; - return val; -} - -/* call kctl->put with the given value(s) */ -static int put_kctl_with_value(struct snd_kcontrol *kctl, int val) -{ - struct snd_ctl_elem_value *ucontrol; - ucontrol = kzalloc(sizeof(*ucontrol), GFP_KERNEL); - if (!ucontrol) - return -ENOMEM; - ucontrol->value.integer.value[0] = val; - ucontrol->value.integer.value[1] = val; - kctl->put(kctl, ucontrol); - kfree(ucontrol); - return 0; -} - -/* initialize the slave volume with 0dB */ -static int init_slave_0dB(void *data, struct snd_kcontrol *slave) -{ - int offset = get_kctl_0dB_offset(slave); - if (offset > 0) - put_kctl_with_value(slave, offset); - return 0; -} - -/* unmute the slave */ -static int init_slave_unmute(void *data, struct snd_kcontrol *slave) -{ - return put_kctl_with_value(slave, 1); -} - -/** - * snd_hda_add_vmaster - create a virtual master control and add slaves - * @codec: HD-audio codec - * @name: vmaster control name - * @tlv: TLV data (optional) - * @slaves: slave control names (optional) - * @suffix: suffix string to each slave name (optional) - * @init_slave_vol: initialize slaves to unmute/0dB - * @ctl_ret: store the vmaster kcontrol in return - * - * Create a virtual master control with the given name. The TLV data - * must be either NULL or a valid data. - * - * @slaves is a NULL-terminated array of strings, each of which is a - * slave control name. All controls with these names are assigned to - * the new virtual master control. - * - * This function returns zero if successful or a negative error code. - */ -int __snd_hda_add_vmaster(struct hda_codec *codec, char *name, - unsigned int *tlv, const char * const *slaves, - const char *suffix, bool init_slave_vol, - struct snd_kcontrol **ctl_ret) -{ - struct snd_kcontrol *kctl; - int err; - - if (ctl_ret) - *ctl_ret = NULL; - - err = map_slaves(codec, slaves, suffix, check_slave_present, NULL); - if (err != 1) { - snd_printdd("No slave found for %s\n", name); - return 0; - } - kctl = snd_ctl_make_virtual_master(name, tlv); - if (!kctl) - return -ENOMEM; - err = snd_hda_ctl_add(codec, 0, kctl); - if (err < 0) - return err; - - err = map_slaves(codec, slaves, suffix, - (map_slave_func_t)snd_ctl_add_slave, kctl); - if (err < 0) - return err; - - /* init with master mute & zero volume */ - put_kctl_with_value(kctl, 0); - if (init_slave_vol) - map_slaves(codec, slaves, suffix, - tlv ? init_slave_0dB : init_slave_unmute, kctl); - - if (ctl_ret) - *ctl_ret = kctl; - return 0; -} -EXPORT_SYMBOL_HDA(__snd_hda_add_vmaster); - -/* - * mute-LED control using vmaster - */ -static int vmaster_mute_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - static const char * const texts[] = { - "Off", "On", "Follow Master" - }; - unsigned int index; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - index = uinfo->value.enumerated.item; - if (index >= 3) - index = 2; - strcpy(uinfo->value.enumerated.name, texts[index]); - return 0; -} - -static int vmaster_mute_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_vmaster_mute_hook *hook = snd_kcontrol_chip(kcontrol); - ucontrol->value.enumerated.item[0] = hook->mute_mode; - return 0; -} - -static int vmaster_mute_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_vmaster_mute_hook *hook = snd_kcontrol_chip(kcontrol); - unsigned int old_mode = hook->mute_mode; - - hook->mute_mode = ucontrol->value.enumerated.item[0]; - if (hook->mute_mode > HDA_VMUTE_FOLLOW_MASTER) - hook->mute_mode = HDA_VMUTE_FOLLOW_MASTER; - if (old_mode == hook->mute_mode) - return 0; - snd_hda_sync_vmaster_hook(hook); - return 1; -} - -static struct snd_kcontrol_new vmaster_mute_mode = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Mute-LED Mode", - .info = vmaster_mute_mode_info, - .get = vmaster_mute_mode_get, - .put = vmaster_mute_mode_put, -}; - -/* - * Add a mute-LED hook with the given vmaster switch kctl - * "Mute-LED Mode" control is automatically created and associated with - * the given hook. - */ -int snd_hda_add_vmaster_hook(struct hda_codec *codec, - struct hda_vmaster_mute_hook *hook, - bool expose_enum_ctl) -{ - struct snd_kcontrol *kctl; - - if (!hook->hook || !hook->sw_kctl) - return 0; - snd_ctl_add_vmaster_hook(hook->sw_kctl, hook->hook, codec); - hook->codec = codec; - hook->mute_mode = HDA_VMUTE_FOLLOW_MASTER; - if (!expose_enum_ctl) - return 0; - kctl = snd_ctl_new1(&vmaster_mute_mode, hook); - if (!kctl) - return -ENOMEM; - return snd_hda_ctl_add(codec, 0, kctl); -} -EXPORT_SYMBOL_HDA(snd_hda_add_vmaster_hook); - -/* - * Call the hook with the current value for synchronization - * Should be called in init callback - */ -void snd_hda_sync_vmaster_hook(struct hda_vmaster_mute_hook *hook) -{ - if (!hook->hook || !hook->codec) - return; - switch (hook->mute_mode) { - case HDA_VMUTE_FOLLOW_MASTER: - snd_ctl_sync_vmaster_hook(hook->sw_kctl); - break; - default: - hook->hook(hook->codec, hook->mute_mode); - break; - } -} -EXPORT_SYMBOL_HDA(snd_hda_sync_vmaster_hook); - - -/** - * snd_hda_mixer_amp_switch_info - Info callback for a standard AMP mixer switch - * - * The control element is supposed to have the private_value field - * set up via HDA_COMPOSE_AMP_VAL*() or related macros. - */ -int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - int chs = get_amp_channels(kcontrol); - - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = chs == 3 ? 2 : 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_info); - -/** - * snd_hda_mixer_amp_switch_get - Get callback for a standard AMP mixer switch - * - * The control element is supposed to have the private_value field - * set up via HDA_COMPOSE_AMP_VAL*() or related macros. - */ -int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = get_amp_nid(kcontrol); - int chs = get_amp_channels(kcontrol); - int dir = get_amp_direction(kcontrol); - int idx = get_amp_index(kcontrol); - long *valp = ucontrol->value.integer.value; - - if (chs & 1) - *valp++ = (snd_hda_codec_amp_read(codec, nid, 0, dir, idx) & - HDA_AMP_MUTE) ? 0 : 1; - if (chs & 2) - *valp = (snd_hda_codec_amp_read(codec, nid, 1, dir, idx) & - HDA_AMP_MUTE) ? 0 : 1; - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_get); - -/** - * snd_hda_mixer_amp_switch_put - Put callback for a standard AMP mixer switch - * - * The control element is supposed to have the private_value field - * set up via HDA_COMPOSE_AMP_VAL*() or related macros. - */ -int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = get_amp_nid(kcontrol); - int chs = get_amp_channels(kcontrol); - int dir = get_amp_direction(kcontrol); - int idx = get_amp_index(kcontrol); - long *valp = ucontrol->value.integer.value; - int change = 0; - - snd_hda_power_up(codec); - if (chs & 1) { - change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx, - HDA_AMP_MUTE, - *valp ? 0 : HDA_AMP_MUTE); - valp++; - } - if (chs & 2) - change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx, - HDA_AMP_MUTE, - *valp ? 0 : HDA_AMP_MUTE); - hda_call_check_power_status(codec, nid); - snd_hda_power_down(codec); - return change; -} -EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put); - -#ifdef CONFIG_SND_HDA_INPUT_BEEP -/** - * snd_hda_mixer_amp_switch_put_beep - Put callback for a beep AMP switch - * - * This function calls snd_hda_enable_beep_device(), which behaves differently - * depending on beep_mode option. - */ -int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - long *valp = ucontrol->value.integer.value; - - snd_hda_enable_beep_device(codec, *valp); - return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); -} -EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put_beep); -#endif /* CONFIG_SND_HDA_INPUT_BEEP */ - -/* - * bound volume controls - * - * bind multiple volumes (# indices, from 0) - */ - -#define AMP_VAL_IDX_SHIFT 19 -#define AMP_VAL_IDX_MASK (0x0f<<19) - -/** - * snd_hda_mixer_bind_switch_get - Get callback for a bound volume control - * - * The control element is supposed to have the private_value field - * set up via HDA_BIND_MUTE*() macros. - */ -int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned long pval; - int err; - - mutex_lock(&codec->control_mutex); - pval = kcontrol->private_value; - kcontrol->private_value = pval & ~AMP_VAL_IDX_MASK; /* index 0 */ - err = snd_hda_mixer_amp_switch_get(kcontrol, ucontrol); - kcontrol->private_value = pval; - mutex_unlock(&codec->control_mutex); - return err; -} -EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_get); - -/** - * snd_hda_mixer_bind_switch_put - Put callback for a bound volume control - * - * The control element is supposed to have the private_value field - * set up via HDA_BIND_MUTE*() macros. - */ -int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned long pval; - int i, indices, err = 0, change = 0; - - mutex_lock(&codec->control_mutex); - pval = kcontrol->private_value; - indices = (pval & AMP_VAL_IDX_MASK) >> AMP_VAL_IDX_SHIFT; - for (i = 0; i < indices; i++) { - kcontrol->private_value = (pval & ~AMP_VAL_IDX_MASK) | - (i << AMP_VAL_IDX_SHIFT); - err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); - if (err < 0) - break; - change |= err; - } - kcontrol->private_value = pval; - mutex_unlock(&codec->control_mutex); - return err < 0 ? err : change; -} -EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_put); - -/** - * snd_hda_mixer_bind_ctls_info - Info callback for a generic bound control - * - * The control element is supposed to have the private_value field - * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros. - */ -int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_bind_ctls *c; - int err; - - mutex_lock(&codec->control_mutex); - c = (struct hda_bind_ctls *)kcontrol->private_value; - kcontrol->private_value = *c->values; - err = c->ops->info(kcontrol, uinfo); - kcontrol->private_value = (long)c; - mutex_unlock(&codec->control_mutex); - return err; -} -EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_info); - -/** - * snd_hda_mixer_bind_ctls_get - Get callback for a generic bound control - * - * The control element is supposed to have the private_value field - * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros. - */ -int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_bind_ctls *c; - int err; - - mutex_lock(&codec->control_mutex); - c = (struct hda_bind_ctls *)kcontrol->private_value; - kcontrol->private_value = *c->values; - err = c->ops->get(kcontrol, ucontrol); - kcontrol->private_value = (long)c; - mutex_unlock(&codec->control_mutex); - return err; -} -EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_get); - -/** - * snd_hda_mixer_bind_ctls_put - Put callback for a generic bound control - * - * The control element is supposed to have the private_value field - * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros. - */ -int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_bind_ctls *c; - unsigned long *vals; - int err = 0, change = 0; - - mutex_lock(&codec->control_mutex); - c = (struct hda_bind_ctls *)kcontrol->private_value; - for (vals = c->values; *vals; vals++) { - kcontrol->private_value = *vals; - err = c->ops->put(kcontrol, ucontrol); - if (err < 0) - break; - change |= err; - } - kcontrol->private_value = (long)c; - mutex_unlock(&codec->control_mutex); - return err < 0 ? err : change; -} -EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_put); - -/** - * snd_hda_mixer_bind_tlv - TLV callback for a generic bound control - * - * The control element is supposed to have the private_value field - * set up via HDA_BIND_VOL() macro. - */ -int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag, - unsigned int size, unsigned int __user *tlv) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_bind_ctls *c; - int err; - - mutex_lock(&codec->control_mutex); - c = (struct hda_bind_ctls *)kcontrol->private_value; - kcontrol->private_value = *c->values; - err = c->ops->tlv(kcontrol, op_flag, size, tlv); - kcontrol->private_value = (long)c; - mutex_unlock(&codec->control_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, - .get = snd_hda_mixer_amp_volume_get, - .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, - .get = snd_hda_mixer_amp_switch_get, - .put = snd_hda_mixer_amp_switch_put, - .tlv = snd_hda_mixer_amp_tlv -}; -EXPORT_SYMBOL_HDA(snd_hda_bind_sw); - -/* - * SPDIF out controls - */ - -static int snd_hda_spdif_mask_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; - uinfo->count = 1; - return 0; -} - -static int snd_hda_spdif_cmask_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL | - IEC958_AES0_NONAUDIO | - IEC958_AES0_CON_EMPHASIS_5015 | - IEC958_AES0_CON_NOT_COPYRIGHT; - ucontrol->value.iec958.status[1] = IEC958_AES1_CON_CATEGORY | - IEC958_AES1_CON_ORIGINAL; - return 0; -} - -static int snd_hda_spdif_pmask_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL | - IEC958_AES0_NONAUDIO | - IEC958_AES0_PRO_EMPHASIS_5015; - return 0; -} - -static int snd_hda_spdif_default_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - int idx = kcontrol->private_value; - struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx); - - ucontrol->value.iec958.status[0] = spdif->status & 0xff; - ucontrol->value.iec958.status[1] = (spdif->status >> 8) & 0xff; - ucontrol->value.iec958.status[2] = (spdif->status >> 16) & 0xff; - ucontrol->value.iec958.status[3] = (spdif->status >> 24) & 0xff; - - return 0; -} - -/* convert from SPDIF status bits to HDA SPDIF bits - * bit 0 (DigEn) is always set zero (to be filled later) - */ -static unsigned short convert_from_spdif_status(unsigned int sbits) -{ - unsigned short val = 0; - - if (sbits & IEC958_AES0_PROFESSIONAL) - val |= AC_DIG1_PROFESSIONAL; - if (sbits & IEC958_AES0_NONAUDIO) - val |= AC_DIG1_NONAUDIO; - if (sbits & IEC958_AES0_PROFESSIONAL) { - if ((sbits & IEC958_AES0_PRO_EMPHASIS) == - IEC958_AES0_PRO_EMPHASIS_5015) - val |= AC_DIG1_EMPHASIS; - } else { - if ((sbits & IEC958_AES0_CON_EMPHASIS) == - IEC958_AES0_CON_EMPHASIS_5015) - val |= AC_DIG1_EMPHASIS; - if (!(sbits & IEC958_AES0_CON_NOT_COPYRIGHT)) - val |= AC_DIG1_COPYRIGHT; - if (sbits & (IEC958_AES1_CON_ORIGINAL << 8)) - val |= AC_DIG1_LEVEL; - val |= sbits & (IEC958_AES1_CON_CATEGORY << 8); - } - return val; -} - -/* convert to SPDIF status bits from HDA SPDIF bits - */ -static unsigned int convert_to_spdif_status(unsigned short val) -{ - unsigned int sbits = 0; - - if (val & AC_DIG1_NONAUDIO) - sbits |= IEC958_AES0_NONAUDIO; - if (val & AC_DIG1_PROFESSIONAL) - sbits |= IEC958_AES0_PROFESSIONAL; - if (sbits & IEC958_AES0_PROFESSIONAL) { - if (sbits & AC_DIG1_EMPHASIS) - sbits |= IEC958_AES0_PRO_EMPHASIS_5015; - } else { - if (val & AC_DIG1_EMPHASIS) - sbits |= IEC958_AES0_CON_EMPHASIS_5015; - if (!(val & AC_DIG1_COPYRIGHT)) - sbits |= IEC958_AES0_CON_NOT_COPYRIGHT; - if (val & AC_DIG1_LEVEL) - sbits |= (IEC958_AES1_CON_ORIGINAL << 8); - sbits |= val & (0x7f << 8); - } - return sbits; -} - -/* set digital convert verbs both for the given NID and its slaves */ -static void set_dig_out(struct hda_codec *codec, hda_nid_t nid, - int verb, int val) -{ - const hda_nid_t *d; - - 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_cache(codec, *d, 0, verb, val); -} - -static inline void set_dig_out_convert(struct hda_codec *codec, hda_nid_t nid, - int dig1, int dig2) -{ - if (dig1 != -1) - set_dig_out(codec, nid, AC_VERB_SET_DIGI_CONVERT_1, dig1); - if (dig2 != -1) - set_dig_out(codec, nid, AC_VERB_SET_DIGI_CONVERT_2, dig2); -} - -static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - int idx = kcontrol->private_value; - struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx); - hda_nid_t nid = spdif->nid; - unsigned short val; - int change; - - mutex_lock(&codec->spdif_mutex); - spdif->status = ucontrol->value.iec958.status[0] | - ((unsigned int)ucontrol->value.iec958.status[1] << 8) | - ((unsigned int)ucontrol->value.iec958.status[2] << 16) | - ((unsigned int)ucontrol->value.iec958.status[3] << 24); - val = convert_from_spdif_status(spdif->status); - val |= spdif->ctls & 1; - change = spdif->ctls != val; - spdif->ctls = val; - if (change && nid != (u16)-1) - set_dig_out_convert(codec, nid, val & 0xff, (val >> 8) & 0xff); - mutex_unlock(&codec->spdif_mutex); - return change; -} - -#define snd_hda_spdif_out_switch_info snd_ctl_boolean_mono_info - -static int snd_hda_spdif_out_switch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - int idx = kcontrol->private_value; - struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx); - - ucontrol->value.integer.value[0] = spdif->ctls & AC_DIG1_ENABLE; - return 0; -} - -static inline void set_spdif_ctls(struct hda_codec *codec, hda_nid_t nid, - int dig1, int dig2) -{ - set_dig_out_convert(codec, nid, dig1, dig2); - /* unmute amp switch (if any) */ - if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) && - (dig1 & AC_DIG1_ENABLE)) - snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, - HDA_AMP_MUTE, 0); -} - -static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - int idx = kcontrol->private_value; - struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx); - hda_nid_t nid = spdif->nid; - unsigned short val; - int change; - - mutex_lock(&codec->spdif_mutex); - val = spdif->ctls & ~AC_DIG1_ENABLE; - if (ucontrol->value.integer.value[0]) - val |= AC_DIG1_ENABLE; - change = spdif->ctls != val; - spdif->ctls = val; - if (change && nid != (u16)-1) - set_spdif_ctls(codec, nid, val & 0xff, -1); - mutex_unlock(&codec->spdif_mutex); - return change; -} - -static struct snd_kcontrol_new dig_mixes[] = { - { - .access = SNDRV_CTL_ELEM_ACCESS_READ, - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK), - .info = snd_hda_spdif_mask_info, - .get = snd_hda_spdif_cmask_get, - }, - { - .access = SNDRV_CTL_ELEM_ACCESS_READ, - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PRO_MASK), - .info = snd_hda_spdif_mask_info, - .get = snd_hda_spdif_pmask_get, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), - .info = snd_hda_spdif_mask_info, - .get = snd_hda_spdif_default_get, - .put = snd_hda_spdif_default_put, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH), - .info = snd_hda_spdif_out_switch_info, - .get = snd_hda_spdif_out_switch_get, - .put = snd_hda_spdif_out_switch_put, - }, - { } /* end */ -}; - -/** - * snd_hda_create_spdif_out_ctls - create Output SPDIF-related controls - * @codec: the HDA codec - * @nid: audio out widget NID - * - * Creates controls related with the SPDIF output. - * Called from each patch supporting the SPDIF out. - * - * Returns 0 if successful, or a negative error code. - */ -int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, - hda_nid_t associated_nid, - hda_nid_t cvt_nid) -{ - int err; - struct snd_kcontrol *kctl; - struct snd_kcontrol_new *dig_mix; - int idx; - struct hda_spdif_out *spdif; - - idx = find_empty_mixer_ctl_idx(codec, "IEC958 Playback Switch"); - if (idx < 0) { - printk(KERN_ERR "hda_codec: too many IEC958 outputs\n"); - return -EBUSY; - } - spdif = snd_array_new(&codec->spdif_out); - 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 = codec->spdif_out.used - 1; - err = snd_hda_ctl_add(codec, associated_nid, kctl); - if (err < 0) - return err; - } - spdif->nid = cvt_nid; - spdif->ctls = snd_hda_codec_read(codec, cvt_nid, 0, - AC_VERB_GET_DIGI_CONVERT_1, 0); - spdif->status = convert_to_spdif_status(spdif->ctls); - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_create_spdif_out_ctls); - -struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec, - hda_nid_t nid) -{ - int i; - for (i = 0; i < codec->spdif_out.used; i++) { - struct hda_spdif_out *spdif = - snd_array_elem(&codec->spdif_out, i); - if (spdif->nid == nid) - return spdif; - } - return NULL; -} -EXPORT_SYMBOL_HDA(snd_hda_spdif_out_of_nid); - -void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx) -{ - struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx); - - mutex_lock(&codec->spdif_mutex); - spdif->nid = (u16)-1; - mutex_unlock(&codec->spdif_mutex); -} -EXPORT_SYMBOL_HDA(snd_hda_spdif_ctls_unassign); - -void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid) -{ - struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx); - unsigned short val; - - mutex_lock(&codec->spdif_mutex); - if (spdif->nid != nid) { - spdif->nid = nid; - val = spdif->ctls; - set_spdif_ctls(codec, nid, val & 0xff, (val >> 8) & 0xff); - } - mutex_unlock(&codec->spdif_mutex); -} -EXPORT_SYMBOL_HDA(snd_hda_spdif_ctls_assign); - -/* - * SPDIF sharing with analog output - */ -static int spdif_share_sw_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol); - ucontrol->value.integer.value[0] = mout->share_spdif; - return 0; -} - -static int spdif_share_sw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol); - mout->share_spdif = !!ucontrol->value.integer.value[0]; - return 0; -} - -static struct snd_kcontrol_new spdif_share_sw = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "IEC958 Default PCM Playback Switch", - .info = snd_ctl_boolean_mono_info, - .get = spdif_share_sw_get, - .put = spdif_share_sw_put, -}; - -/** - * snd_hda_create_spdif_share_sw - create Default PCM switch - * @codec: the HDA codec - * @mout: multi-out instance - */ -int snd_hda_create_spdif_share_sw(struct hda_codec *codec, - struct hda_multi_out *mout) -{ - if (!mout->dig_out_nid) - return 0; - /* ATTENTION: here mout is passed as private_data, instead of codec */ - return snd_hda_ctl_add(codec, mout->dig_out_nid, - snd_ctl_new1(&spdif_share_sw, mout)); -} -EXPORT_SYMBOL_HDA(snd_hda_create_spdif_share_sw); - -/* - * SPDIF input - */ - -#define snd_hda_spdif_in_switch_info snd_hda_spdif_out_switch_info - -static int snd_hda_spdif_in_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->spdif_in_enable; - return 0; -} - -static int snd_hda_spdif_in_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value; - unsigned int val = !!ucontrol->value.integer.value[0]; - int change; - - mutex_lock(&codec->spdif_mutex); - change = codec->spdif_in_enable != val; - if (change) { - codec->spdif_in_enable = val; - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_DIGI_CONVERT_1, val); - } - mutex_unlock(&codec->spdif_mutex); - return change; -} - -static int snd_hda_spdif_in_status_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value; - unsigned short val; - unsigned int sbits; - - val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT_1, 0); - sbits = convert_to_spdif_status(val); - ucontrol->value.iec958.status[0] = sbits; - ucontrol->value.iec958.status[1] = sbits >> 8; - ucontrol->value.iec958.status[2] = sbits >> 16; - ucontrol->value.iec958.status[3] = sbits >> 24; - return 0; -} - -static struct snd_kcontrol_new dig_in_ctls[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, SWITCH), - .info = snd_hda_spdif_in_switch_info, - .get = snd_hda_spdif_in_switch_get, - .put = snd_hda_spdif_in_switch_put, - }, - { - .access = SNDRV_CTL_ELEM_ACCESS_READ, - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT), - .info = snd_hda_spdif_mask_info, - .get = snd_hda_spdif_in_status_get, - }, - { } /* end */ -}; - -/** - * snd_hda_create_spdif_in_ctls - create Input SPDIF-related controls - * @codec: the HDA codec - * @nid: audio in widget NID - * - * Creates controls related with the SPDIF input. - * Called from each patch supporting the SPDIF in. - * - * Returns 0 if successful, or a negative error code. - */ -int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid) -{ - int err; - struct snd_kcontrol *kctl; - struct snd_kcontrol_new *dig_mix; - int idx; - - idx = find_empty_mixer_ctl_idx(codec, "IEC958 Capture Switch"); - if (idx < 0) { - printk(KERN_ERR "hda_codec: too many IEC958 inputs\n"); - return -EBUSY; - } - for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) { - kctl = snd_ctl_new1(dig_mix, codec); - if (!kctl) - return -ENOMEM; - kctl->private_value = nid; - err = snd_hda_ctl_add(codec, nid, kctl); - if (err < 0) - return err; - } - codec->spdif_in_enable = - snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_DIGI_CONVERT_1, 0) & - AC_DIG1_ENABLE; - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls); - -#ifdef CONFIG_PM -/* - * command cache - */ - -/* build a 32bit cache key with the widget id and the command parameter */ -#define build_cmd_cache_key(nid, verb) ((verb << 8) | nid) -#define get_cmd_cache_nid(key) ((key) & 0xff) -#define get_cmd_cache_cmd(key) (((key) >> 8) & 0xffff) - -/** - * snd_hda_codec_write_cache - send a single command with caching - * @codec: the HDA codec - * @nid: NID to send the command - * @direct: direct flag - * @verb: the verb to send - * @parm: the parameter for the verb - * - * Send a single command without waiting for response. - * - * Returns 0 if successful, or a negative error code. - */ -int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, - int direct, unsigned int verb, unsigned int parm) -{ - int err = snd_hda_codec_write(codec, nid, direct, verb, parm); - struct hda_cache_head *c; - u32 key; - - if (err < 0) - return err; - /* parm may contain the verb stuff for get/set amp */ - verb = verb | (parm >> 8); - parm &= 0xff; - key = build_cmd_cache_key(nid, verb); - mutex_lock(&codec->bus->cmd_mutex); - c = get_alloc_hash(&codec->cmd_cache, key); - if (c) - c->val = parm; - mutex_unlock(&codec->bus->cmd_mutex); - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache); - -/** - * snd_hda_codec_update_cache - check cache and write the cmd only when needed - * @codec: the HDA codec - * @nid: NID to send the command - * @direct: direct flag - * @verb: the verb to send - * @parm: the parameter for the verb - * - * This function works like snd_hda_codec_write_cache(), but it doesn't send - * command if the parameter is already identical with the cached value. - * If not, it sends the command and refreshes the cache. - * - * Returns 0 if successful, or a negative error code. - */ -int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid, - int direct, unsigned int verb, unsigned int parm) -{ - struct hda_cache_head *c; - u32 key; - - /* parm may contain the verb stuff for get/set amp */ - verb = verb | (parm >> 8); - parm &= 0xff; - key = build_cmd_cache_key(nid, verb); - mutex_lock(&codec->bus->cmd_mutex); - c = get_hash(&codec->cmd_cache, key); - if (c && c->val == parm) { - mutex_unlock(&codec->bus->cmd_mutex); - return 0; - } - mutex_unlock(&codec->bus->cmd_mutex); - return snd_hda_codec_write_cache(codec, nid, direct, verb, parm); -} -EXPORT_SYMBOL_HDA(snd_hda_codec_update_cache); - -/** - * snd_hda_codec_resume_cache - Resume the all commands from the cache - * @codec: HD-audio codec - * - * Execute all verbs recorded in the command caches to resume. - */ -void snd_hda_codec_resume_cache(struct hda_codec *codec) -{ - struct hda_cache_head *buffer = codec->cmd_cache.buf.list; - int i; - - for (i = 0; i < codec->cmd_cache.buf.used; i++, buffer++) { - u32 key = buffer->key; - if (!key) - continue; - snd_hda_codec_write(codec, get_cmd_cache_nid(key), 0, - get_cmd_cache_cmd(key), buffer->val); - } -} -EXPORT_SYMBOL_HDA(snd_hda_codec_resume_cache); - -/** - * snd_hda_sequence_write_cache - sequence writes with caching - * @codec: the HDA codec - * @seq: VERB array to send - * - * Send the commands sequentially from the given array. - * Thte commands are recorded on cache for power-save and resume. - * The array must be terminated with NID=0. - */ -void snd_hda_sequence_write_cache(struct hda_codec *codec, - const struct hda_verb *seq) -{ - for (; seq->nid; seq++) - snd_hda_codec_write_cache(codec, seq->nid, 0, seq->verb, - seq->param); -} -EXPORT_SYMBOL_HDA(snd_hda_sequence_write_cache); -#endif /* CONFIG_PM */ - -void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, - unsigned int power_state, - bool eapd_workaround) -{ - hda_nid_t nid = codec->start_nid; - int i; - - for (i = 0; i < codec->num_nodes; i++, nid++) { - unsigned int wcaps = get_wcaps(codec, nid); - if (!(wcaps & AC_WCAP_POWER)) - continue; - /* don't power down the widget if it controls eapd and - * EAPD_BTLENABLE is set. - */ - if (eapd_workaround && power_state == AC_PWRST_D3 && - get_wcaps_type(wcaps) == AC_WID_PIN && - (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) { - int eapd = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_EAPD_BTLENABLE, 0); - if (eapd & 0x02) - continue; - } - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, - power_state); - } - - if (power_state == AC_PWRST_D0) { - unsigned long end_time; - int state; - /* wait until the codec reachs to D0 */ - end_time = jiffies + msecs_to_jiffies(500); - do { - state = snd_hda_codec_read(codec, fg, 0, - AC_VERB_GET_POWER_STATE, 0); - if (state == power_state) - break; - msleep(1); - } while (time_after_eq(end_time, jiffies)); - } -} -EXPORT_SYMBOL_HDA(snd_hda_codec_set_power_to_all); - -/* - * set power state of the codec - */ -static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, - unsigned int power_state) -{ - if (codec->patch_ops.set_power_state) { - codec->patch_ops.set_power_state(codec, fg, power_state); - return; - } - - /* this delay seems necessary to avoid click noise at power-down */ - if (power_state == AC_PWRST_D3) - msleep(100); - snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, - power_state); - snd_hda_codec_set_power_to_all(codec, fg, power_state, true); -} - -#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 CONFIG_PM -/* - * call suspend and power-down; used both from PM and power-save - */ -static void hda_call_codec_suspend(struct hda_codec *codec) -{ - if (codec->patch_ops.suspend) - codec->patch_ops.suspend(codec, PMSG_SUSPEND); - hda_cleanup_all_streams(codec); - hda_set_power_state(codec, - codec->afg ? codec->afg : codec->mfg, - AC_PWRST_D3); -#ifdef CONFIG_SND_HDA_POWER_SAVE - snd_hda_update_power_acct(codec); - cancel_delayed_work(&codec->power_work); - codec->power_on = 0; - codec->power_transition = 0; - codec->power_jiffies = jiffies; -#endif -} - -/* - * kick up codec; used both from PM and power-save - */ -static void hda_call_codec_resume(struct hda_codec *codec) -{ - hda_set_power_state(codec, - codec->afg ? codec->afg : codec->mfg, - AC_PWRST_D0); - restore_pincfgs(codec); /* restore all current pin configs */ - restore_shutup_pins(codec); - hda_exec_init_verbs(codec); - snd_hda_jack_set_dirty_all(codec); - if (codec->patch_ops.resume) - codec->patch_ops.resume(codec); - else { - if (codec->patch_ops.init) - codec->patch_ops.init(codec); - snd_hda_codec_resume_amp(codec); - snd_hda_codec_resume_cache(codec); - } -} -#endif /* CONFIG_PM */ - - -/** - * snd_hda_build_controls - build mixer controls - * @bus: the BUS - * - * Creates mixer controls for each codec included in the bus. - * - * Returns 0 if successful, otherwise a negative error code. - */ -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 = snd_hda_codec_build_controls(codec); - if (err < 0) { - printk(KERN_ERR "hda_codec: cannot build controls " - "for #%d (error %d)\n", codec->addr, err); - err = snd_hda_codec_reset(codec); - if (err < 0) { - printk(KERN_ERR - "hda_codec: cannot revert codec\n"); - return err; - } - } - } - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_build_controls); - -int snd_hda_codec_build_controls(struct hda_codec *codec) -{ - int err = 0; - 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); - if (err < 0) - return err; - return 0; -} - -/* - * stream formats - */ -struct hda_rate_tbl { - unsigned int hz; - unsigned int alsa_bits; - unsigned int hda_fmt; -}; - -/* rate = base * mult / div */ -#define HDA_RATE(base, mult, div) \ - (AC_FMT_BASE_##base##K | (((mult) - 1) << AC_FMT_MULT_SHIFT) | \ - (((div) - 1) << AC_FMT_DIV_SHIFT)) - -static struct hda_rate_tbl rate_bits[] = { - /* rate in Hz, ALSA rate bitmask, HDA format value */ - - /* autodetected value used in snd_hda_query_supported_pcm */ - { 8000, SNDRV_PCM_RATE_8000, HDA_RATE(48, 1, 6) }, - { 11025, SNDRV_PCM_RATE_11025, HDA_RATE(44, 1, 4) }, - { 16000, SNDRV_PCM_RATE_16000, HDA_RATE(48, 1, 3) }, - { 22050, SNDRV_PCM_RATE_22050, HDA_RATE(44, 1, 2) }, - { 32000, SNDRV_PCM_RATE_32000, HDA_RATE(48, 2, 3) }, - { 44100, SNDRV_PCM_RATE_44100, HDA_RATE(44, 1, 1) }, - { 48000, SNDRV_PCM_RATE_48000, HDA_RATE(48, 1, 1) }, - { 88200, SNDRV_PCM_RATE_88200, HDA_RATE(44, 2, 1) }, - { 96000, SNDRV_PCM_RATE_96000, HDA_RATE(48, 2, 1) }, - { 176400, SNDRV_PCM_RATE_176400, HDA_RATE(44, 4, 1) }, - { 192000, SNDRV_PCM_RATE_192000, HDA_RATE(48, 4, 1) }, -#define AC_PAR_PCM_RATE_BITS 11 - /* up to bits 10, 384kHZ isn't supported properly */ - - /* not autodetected value */ - { 9600, SNDRV_PCM_RATE_KNOT, HDA_RATE(48, 1, 5) }, - - { 0 } /* terminator */ -}; - -/** - * snd_hda_calc_stream_format - calculate format bitset - * @rate: the sample rate - * @channels: the number of channels - * @format: the PCM format (SNDRV_PCM_FORMAT_XXX) - * @maxbps: the max. bps - * - * Calculate the format bitset from the given rate, channels and th PCM format. - * - * Return zero if invalid. - */ -unsigned int snd_hda_calc_stream_format(unsigned int rate, - unsigned int channels, - unsigned int format, - unsigned int maxbps, - unsigned short spdif_ctls) -{ - int i; - unsigned int val = 0; - - for (i = 0; rate_bits[i].hz; i++) - if (rate_bits[i].hz == rate) { - val = rate_bits[i].hda_fmt; - break; - } - if (!rate_bits[i].hz) { - snd_printdd("invalid rate %d\n", rate); - return 0; - } - - if (channels == 0 || channels > 8) { - snd_printdd("invalid channels %d\n", channels); - return 0; - } - val |= channels - 1; - - switch (snd_pcm_format_width(format)) { - case 8: - val |= AC_FMT_BITS_8; - break; - case 16: - val |= AC_FMT_BITS_16; - break; - case 20: - case 24: - case 32: - if (maxbps >= 32 || format == SNDRV_PCM_FORMAT_FLOAT_LE) - val |= AC_FMT_BITS_32; - else if (maxbps >= 24) - val |= AC_FMT_BITS_24; - else - val |= AC_FMT_BITS_20; - break; - default: - snd_printdd("invalid format width %d\n", - snd_pcm_format_width(format)); - return 0; - } - - if (spdif_ctls & AC_DIG1_NONAUDIO) - val |= AC_FMT_TYPE_NON_PCM; - - return val; -} -EXPORT_SYMBOL_HDA(snd_hda_calc_stream_format); - -static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int val = 0; - if (nid != codec->afg && - (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) - val = snd_hda_param_read(codec, nid, AC_PAR_PCM); - if (!val || val == -1) - val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM); - if (!val || val == -1) - return 0; - return val; -} - -static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid) -{ - return query_caps_hash(codec, nid, HDA_HASH_PARPCM_KEY(nid), - get_pcm_param); -} - -static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM); - if (!streams || streams == -1) - streams = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM); - if (!streams || streams == -1) - return 0; - return streams; -} - -static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid) -{ - return query_caps_hash(codec, nid, HDA_HASH_PARSTR_KEY(nid), - get_stream_param); -} - -/** - * snd_hda_query_supported_pcm - query the supported PCM rates and formats - * @codec: the HDA codec - * @nid: NID to query - * @ratesp: the pointer to store the detected rate bitflags - * @formatsp: the pointer to store the detected formats - * @bpsp: the pointer to store the detected format widths - * - * Queries the supported PCM rates and formats. The NULL @ratesp, @formatsp - * or @bsps argument is ignored. - * - * Returns 0 if successful, otherwise a negative error code. - */ -int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, - u32 *ratesp, u64 *formatsp, unsigned int *bpsp) -{ - unsigned int i, val, wcaps; - - wcaps = get_wcaps(codec, nid); - val = query_pcm_param(codec, nid); - - if (ratesp) { - u32 rates = 0; - for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++) { - if (val & (1 << i)) - rates |= rate_bits[i].alsa_bits; - } - if (rates == 0) { - snd_printk(KERN_ERR "hda_codec: rates == 0 " - "(nid=0x%x, val=0x%x, ovrd=%i)\n", - nid, val, - (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0); - return -EIO; - } - *ratesp = rates; - } - - if (formatsp || bpsp) { - u64 formats = 0; - unsigned int streams, bps; - - streams = query_stream_param(codec, nid); - if (!streams) - return -EIO; - - bps = 0; - if (streams & AC_SUPFMT_PCM) { - if (val & AC_SUPPCM_BITS_8) { - formats |= SNDRV_PCM_FMTBIT_U8; - bps = 8; - } - if (val & AC_SUPPCM_BITS_16) { - formats |= SNDRV_PCM_FMTBIT_S16_LE; - bps = 16; - } - if (wcaps & AC_WCAP_DIGITAL) { - if (val & AC_SUPPCM_BITS_32) - formats |= SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE; - if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24)) - formats |= SNDRV_PCM_FMTBIT_S32_LE; - if (val & AC_SUPPCM_BITS_24) - bps = 24; - else if (val & AC_SUPPCM_BITS_20) - bps = 20; - } else if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24| - AC_SUPPCM_BITS_32)) { - formats |= SNDRV_PCM_FMTBIT_S32_LE; - if (val & AC_SUPPCM_BITS_32) - bps = 32; - else if (val & AC_SUPPCM_BITS_24) - bps = 24; - else if (val & AC_SUPPCM_BITS_20) - bps = 20; - } - } - if (streams & AC_SUPFMT_FLOAT32) { - formats |= SNDRV_PCM_FMTBIT_FLOAT_LE; - if (!bps) - bps = 32; - } - if (streams == AC_SUPFMT_AC3) { - /* should be exclusive */ - /* temporary hack: we have still no proper support - * for the direct AC3 stream... - */ - formats |= SNDRV_PCM_FMTBIT_U8; - bps = 8; - } - if (formats == 0) { - snd_printk(KERN_ERR "hda_codec: formats == 0 " - "(nid=0x%x, val=0x%x, ovrd=%i, " - "streams=0x%x)\n", - nid, val, - (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0, - streams); - return -EIO; - } - if (formatsp) - *formatsp = formats; - if (bpsp) - *bpsp = bps; - } - - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_query_supported_pcm); - -/** - * snd_hda_is_supported_format - Check the validity of the format - * @codec: HD-audio codec - * @nid: NID to check - * @format: the HD-audio format value to check - * - * Check whether the given node supports the format value. - * - * Returns 1 if supported, 0 if not. - */ -int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid, - unsigned int format) -{ - int i; - unsigned int val = 0, rate, stream; - - val = query_pcm_param(codec, nid); - if (!val) - return 0; - - rate = format & 0xff00; - for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++) - if (rate_bits[i].hda_fmt == rate) { - if (val & (1 << i)) - break; - return 0; - } - if (i >= AC_PAR_PCM_RATE_BITS) - return 0; - - stream = query_stream_param(codec, nid); - if (!stream) - return 0; - - if (stream & AC_SUPFMT_PCM) { - switch (format & 0xf0) { - case 0x00: - if (!(val & AC_SUPPCM_BITS_8)) - return 0; - break; - case 0x10: - if (!(val & AC_SUPPCM_BITS_16)) - return 0; - break; - case 0x20: - if (!(val & AC_SUPPCM_BITS_20)) - return 0; - break; - case 0x30: - if (!(val & AC_SUPPCM_BITS_24)) - return 0; - break; - case 0x40: - if (!(val & AC_SUPPCM_BITS_32)) - return 0; - break; - default: - return 0; - } - } else { - /* FIXME: check for float32 and AC3? */ - } - - return 1; -} -EXPORT_SYMBOL_HDA(snd_hda_is_supported_format); - -/* - * PCM stuff - */ -static int hda_pcm_default_open_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - return 0; -} - -static int hda_pcm_default_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); - return 0; -} - -static int hda_pcm_default_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - snd_hda_codec_cleanup_stream(codec, hinfo->nid); - return 0; -} - -static int set_pcm_default_values(struct hda_codec *codec, - struct hda_pcm_stream *info) -{ - int err; - - /* query support PCM information from the given NID */ - if (info->nid && (!info->rates || !info->formats)) { - err = snd_hda_query_supported_pcm(codec, info->nid, - info->rates ? NULL : &info->rates, - info->formats ? NULL : &info->formats, - info->maxbps ? NULL : &info->maxbps); - if (err < 0) - return err; - } - if (info->ops.open == NULL) - info->ops.open = hda_pcm_default_open_close; - if (info->ops.close == NULL) - info->ops.close = hda_pcm_default_open_close; - if (info->ops.prepare == NULL) { - if (snd_BUG_ON(!info->nid)) - return -EINVAL; - info->ops.prepare = hda_pcm_default_prepare; - } - if (info->ops.cleanup == NULL) { - if (snd_BUG_ON(!info->nid)) - return -EINVAL; - info->ops.cleanup = hda_pcm_default_cleanup; - } - return 0; -} - -/* - * codec prepare/cleanup entries - */ -int snd_hda_codec_prepare(struct hda_codec *codec, - struct hda_pcm_stream *hinfo, - unsigned int stream, - unsigned int format, - struct snd_pcm_substream *substream) -{ - int ret; - mutex_lock(&codec->bus->prepare_mutex); - ret = hinfo->ops.prepare(hinfo, codec, stream, format, substream); - if (ret >= 0) - purify_inactive_streams(codec); - mutex_unlock(&codec->bus->prepare_mutex); - return ret; -} -EXPORT_SYMBOL_HDA(snd_hda_codec_prepare); - -void snd_hda_codec_cleanup(struct hda_codec *codec, - struct hda_pcm_stream *hinfo, - struct snd_pcm_substream *substream) -{ - mutex_lock(&codec->bus->prepare_mutex); - hinfo->ops.cleanup(hinfo, codec, substream); - mutex_unlock(&codec->bus->prepare_mutex); -} -EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup); - -/* global */ -const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = { - "Audio", "SPDIF", "HDMI", "Modem" -}; - -/* - * get the empty PCM device number to assign - * - * note the max device number is limited by HDA_MAX_PCMS, currently 10 - */ -static int get_empty_pcm_device(struct hda_bus *bus, int type) -{ - /* audio device indices; not linear to keep compatibility */ - static int audio_idx[HDA_PCM_NTYPES][5] = { - [HDA_PCM_TYPE_AUDIO] = { 0, 2, 4, 5, -1 }, - [HDA_PCM_TYPE_SPDIF] = { 1, -1 }, - [HDA_PCM_TYPE_HDMI] = { 3, 7, 8, 9, -1 }, - [HDA_PCM_TYPE_MODEM] = { 6, -1 }, - }; - int i; - - if (type >= HDA_PCM_NTYPES) { - snd_printk(KERN_WARNING "Invalid PCM type %d\n", type); - return -EINVAL; - } - - for (i = 0; audio_idx[type][i] >= 0 ; i++) - if (!test_and_set_bit(audio_idx[type][i], bus->pcm_dev_bits)) - return audio_idx[type][i]; - - /* non-fixed slots starting from 10 */ - for (i = 10; i < 32; i++) { - if (!test_and_set_bit(i, bus->pcm_dev_bits)) - return i; - } - - snd_printk(KERN_WARNING "Too many %s devices\n", - snd_hda_pcm_type_name[type]); - return -EAGAIN; -} - -/* - * 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) { - printk(KERN_ERR "hda_codec: cannot build PCMs" - "for #%d (error %d)\n", codec->addr, err); - err = snd_hda_codec_reset(codec); - if (err < 0) { - printk(KERN_ERR - "hda_codec: cannot revert codec\n"); - 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) - continue; /* no substreams assigned */ - - if (!cpcm->pcm) { - dev = get_empty_pcm_device(codec->bus, cpcm->pcm_type); - if (dev < 0) - continue; /* no fatal error */ - cpcm->device = dev; - err = snd_hda_attach_pcm(codec, cpcm); - if (err < 0) { - printk(KERN_ERR "hda_codec: cannot attach " - "PCM stream %d for codec #%d\n", - dev, codec->addr); - continue; /* no fatal error */ - } - } - } - return 0; -} - -/** - * snd_hda_build_pcms - build PCM information - * @bus: the BUS - * - * Create PCM information for each codec included in the bus. - * - * The build_pcms codec patch is requested to set up codec->num_pcms and - * codec->pcm_info properly. The array is referred by the top-level driver - * to create its PCM instances. - * The allocated codec->pcm_info should be released in codec->patch_ops.free - * callback. - * - * At least, substreams, channels_min and channels_max must be filled for - * each stream. substreams = 0 indicates that the stream doesn't exist. - * When rates and/or formats are zero, the supported values are queried - * from the given nid. The nid is used also by the default ops.prepare - * and ops.cleanup callbacks. - * - * The driver needs to call ops.open in its open callback. Similarly, - * ops.close is supposed to be called in the close callback. - * ops.prepare should be called in the prepare or hw_params callback - * with the proper parameters for set up. - * ops.cleanup should be called in hw_free for clean up of streams. - * - * This function returns 0 if successful, or a negative error code. - */ -int __devinit snd_hda_build_pcms(struct hda_bus *bus) -{ - struct hda_codec *codec; - - list_for_each_entry(codec, &bus->codec_list, list) { - int err = snd_hda_codec_build_pcms(codec); - 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 - * @codec: the HDA codec - * @num_configs: number of config enums - * @models: array of model name strings - * @tbl: configuration table, terminated by null entries - * - * Compares the modelname or PCI subsystem id of the current codec with the - * given configuration table. If a matching entry is found, returns its - * config value (supposed to be 0 or positive). - * - * If no entries are matching, the function returns a negative value. - */ -int snd_hda_check_board_config(struct hda_codec *codec, - int num_configs, const char * const *models, - const struct snd_pci_quirk *tbl) -{ - if (codec->modelname && models) { - int i; - for (i = 0; i < num_configs; i++) { - if (models[i] && - !strcmp(codec->modelname, models[i])) { - snd_printd(KERN_INFO "hda_codec: model '%s' is " - "selected\n", models[i]); - return i; - } - } - } - - if (!codec->bus->pci || !tbl) - return -1; - - tbl = snd_pci_quirk_lookup(codec->bus->pci, tbl); - if (!tbl) - return -1; - if (tbl->value >= 0 && tbl->value < num_configs) { -#ifdef CONFIG_SND_DEBUG_VERBOSE - char tmp[10]; - const char *model = NULL; - if (models) - model = models[tbl->value]; - if (!model) { - sprintf(tmp, "#%d", tbl->value); - model = tmp; - } - snd_printdd(KERN_INFO "hda_codec: model '%s' is selected " - "for config %x:%x (%s)\n", - model, tbl->subvendor, tbl->subdevice, - (tbl->name ? tbl->name : "Unknown device")); -#endif - return tbl->value; - } - return -1; -} -EXPORT_SYMBOL_HDA(snd_hda_check_board_config); - -/** - * snd_hda_check_board_codec_sid_config - compare the current codec - subsystem ID with the - config table - - This is important for Gateway notebooks with SB450 HDA Audio - where the vendor ID of the PCI device is: - ATI Technologies Inc SB450 HDA Audio [1002:437b] - and the vendor/subvendor are found only at the codec. - - * @codec: the HDA codec - * @num_configs: number of config enums - * @models: array of model name strings - * @tbl: configuration table, terminated by null entries - * - * Compares the modelname or PCI subsystem id of the current codec with the - * given configuration table. If a matching entry is found, returns its - * config value (supposed to be 0 or positive). - * - * If no entries are matching, the function returns a negative value. - */ -int snd_hda_check_board_codec_sid_config(struct hda_codec *codec, - int num_configs, const char * const *models, - const struct snd_pci_quirk *tbl) -{ - const struct snd_pci_quirk *q; - - /* Search for codec ID */ - for (q = tbl; q->subvendor; q++) { - unsigned int mask = 0xffff0000 | q->subdevice_mask; - unsigned int id = (q->subdevice | (q->subvendor << 16)) & mask; - if ((codec->subsystem_id & mask) == id) - break; - } - - if (!q->subvendor) - return -1; - - tbl = q; - - if (tbl->value >= 0 && tbl->value < num_configs) { -#ifdef CONFIG_SND_DEBUG_VERBOSE - char tmp[10]; - const char *model = NULL; - if (models) - model = models[tbl->value]; - if (!model) { - sprintf(tmp, "#%d", tbl->value); - model = tmp; - } - snd_printdd(KERN_INFO "hda_codec: model '%s' is selected " - "for config %x:%x (%s)\n", - model, tbl->subvendor, tbl->subdevice, - (tbl->name ? tbl->name : "Unknown device")); -#endif - return tbl->value; - } - return -1; -} -EXPORT_SYMBOL_HDA(snd_hda_check_board_codec_sid_config); - -/** - * snd_hda_add_new_ctls - create controls from the array - * @codec: the HDA codec - * @knew: the array of struct snd_kcontrol_new - * - * This helper function creates and add new controls in the given array. - * The array must be terminated with an empty entry as terminator. - * - * Returns 0 if successful, or a negative error code. - */ -int snd_hda_add_new_ctls(struct hda_codec *codec, - const struct snd_kcontrol_new *knew) -{ - int err; - - for (; knew->name; knew++) { - struct snd_kcontrol *kctl; - int addr = 0, idx = 0; - if (knew->iface == -1) /* skip this codec private value */ - continue; - for (;;) { - kctl = snd_ctl_new1(knew, codec); - if (!kctl) - return -ENOMEM; - if (addr > 0) - kctl->id.device = addr; - if (idx > 0) - kctl->id.index = idx; - err = snd_hda_ctl_add(codec, 0, kctl); - if (!err) - break; - /* try first with another device index corresponding to - * the codec addr; if it still fails (or it's the - * primary codec), then try another control index - */ - if (!addr && codec->addr) - addr = codec->addr; - else if (!idx && !knew->index) { - idx = find_empty_mixer_ctl_idx(codec, - knew->name); - if (idx <= 0) - return err; - } else - return err; - } - } - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_add_new_ctls); - -#ifdef CONFIG_SND_HDA_POWER_SAVE -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; - return; - } - - trace_hda_power_down(codec); - hda_call_codec_suspend(codec); - if (bus->ops.pm_notify) - bus->ops.pm_notify(bus); -} - -static void hda_keep_power_on(struct hda_codec *codec) -{ - codec->power_count++; - codec->power_on = 1; - codec->power_jiffies = jiffies; -} - -/* update the power on/off account with the current jiffies */ -void snd_hda_update_power_acct(struct hda_codec *codec) -{ - unsigned long delta = jiffies - codec->power_jiffies; - if (codec->power_on) - codec->power_on_acct += delta; - else - codec->power_off_acct += delta; - codec->power_jiffies += delta; -} - -/** - * snd_hda_power_up - Power-up the codec - * @codec: HD-audio codec - * - * Increment the power-up counter and power up the hardware really when - * not turned on yet. - */ -void snd_hda_power_up(struct hda_codec *codec) -{ - struct hda_bus *bus = codec->bus; - - codec->power_count++; - if (codec->power_on || codec->power_transition) - return; - - trace_hda_power_up(codec); - snd_hda_update_power_acct(codec); - codec->power_on = 1; - codec->power_jiffies = jiffies; - if (bus->ops.pm_notify) - bus->ops.pm_notify(bus); - hda_call_codec_resume(codec); - 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) - -/** - * snd_hda_power_down - Power-down the codec - * @codec: HD-audio codec - * - * Decrement the power-up counter and schedules the power-off work if - * the counter rearches to zero. - */ -void snd_hda_power_down(struct hda_codec *codec) -{ - --codec->power_count; - if (!codec->power_on || codec->power_count || codec->power_transition) - return; - if (power_save(codec)) { - codec->power_transition = 1; /* avoid reentrance */ - queue_delayed_work(codec->bus->workq, &codec->power_work, - msecs_to_jiffies(power_save(codec) * 1000)); - } -} -EXPORT_SYMBOL_HDA(snd_hda_power_down); - -/** - * snd_hda_check_amp_list_power - Check the amp list and update the power - * @codec: HD-audio codec - * @check: the object containing an AMP list and the status - * @nid: NID to check / update - * - * Check whether the given NID is in the amp list. If it's in the list, - * check the current AMP status, and update the the power-status according - * to the mute status. - * - * This function is supposed to be set or called from the check_power_status - * patch ops. - */ -int snd_hda_check_amp_list_power(struct hda_codec *codec, - struct hda_loopback_check *check, - hda_nid_t nid) -{ - const struct hda_amp_list *p; - int ch, v; - - if (!check->amplist) - return 0; - for (p = check->amplist; p->nid; p++) { - if (p->nid == nid) - break; - } - if (!p->nid) - return 0; /* nothing changed */ - - for (p = check->amplist; p->nid; p++) { - for (ch = 0; ch < 2; ch++) { - v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir, - p->idx); - if (!(v & HDA_AMP_MUTE) && v > 0) { - if (!check->power_on) { - check->power_on = 1; - snd_hda_power_up(codec); - } - return 1; - } - } - } - if (check->power_on) { - check->power_on = 0; - snd_hda_power_down(codec); - } - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_check_amp_list_power); -#endif - -/* - * Channel mode helper - */ - -/** - * snd_hda_ch_mode_info - Info callback helper for the channel mode enum - */ -int snd_hda_ch_mode_info(struct hda_codec *codec, - struct snd_ctl_elem_info *uinfo, - const struct hda_channel_mode *chmode, - int num_chmodes) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = num_chmodes; - if (uinfo->value.enumerated.item >= num_chmodes) - uinfo->value.enumerated.item = num_chmodes - 1; - sprintf(uinfo->value.enumerated.name, "%dch", - chmode[uinfo->value.enumerated.item].channels); - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_ch_mode_info); - -/** - * snd_hda_ch_mode_get - Get callback helper for the channel mode enum - */ -int snd_hda_ch_mode_get(struct hda_codec *codec, - struct snd_ctl_elem_value *ucontrol, - const struct hda_channel_mode *chmode, - int num_chmodes, - int max_channels) -{ - int i; - - for (i = 0; i < num_chmodes; i++) { - if (max_channels == chmode[i].channels) { - ucontrol->value.enumerated.item[0] = i; - break; - } - } - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_ch_mode_get); - -/** - * snd_hda_ch_mode_put - Put callback helper for the channel mode enum - */ -int snd_hda_ch_mode_put(struct hda_codec *codec, - struct snd_ctl_elem_value *ucontrol, - const struct hda_channel_mode *chmode, - int num_chmodes, - int *max_channelsp) -{ - unsigned int mode; - - mode = ucontrol->value.enumerated.item[0]; - if (mode >= num_chmodes) - return -EINVAL; - if (*max_channelsp == chmode[mode].channels) - return 0; - /* change the current channel setting */ - *max_channelsp = chmode[mode].channels; - if (chmode[mode].sequence) - snd_hda_sequence_write_cache(codec, chmode[mode].sequence); - return 1; -} -EXPORT_SYMBOL_HDA(snd_hda_ch_mode_put); - -/* - * input MUX helper - */ - -/** - * snd_hda_input_mux_info_info - Info callback helper for the input-mux enum - */ -int snd_hda_input_mux_info(const struct hda_input_mux *imux, - struct snd_ctl_elem_info *uinfo) -{ - unsigned int index; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = imux->num_items; - if (!imux->num_items) - return 0; - index = uinfo->value.enumerated.item; - if (index >= imux->num_items) - index = imux->num_items - 1; - strcpy(uinfo->value.enumerated.name, imux->items[index].label); - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_input_mux_info); - -/** - * snd_hda_input_mux_info_put - Put callback helper for the input-mux enum - */ -int snd_hda_input_mux_put(struct hda_codec *codec, - const struct hda_input_mux *imux, - struct snd_ctl_elem_value *ucontrol, - hda_nid_t nid, - unsigned int *cur_val) -{ - unsigned int idx; - - if (!imux->num_items) - return 0; - idx = ucontrol->value.enumerated.item[0]; - if (idx >= imux->num_items) - idx = imux->num_items - 1; - if (*cur_val == idx) - return 0; - snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, - imux->items[idx].index); - *cur_val = idx; - return 1; -} -EXPORT_SYMBOL_HDA(snd_hda_input_mux_put); - - -/* - * Multi-channel / digital-out PCM helper functions - */ - -/* setup SPDIF output stream */ -static void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid, - unsigned int stream_tag, unsigned int format) -{ - struct hda_spdif_out *spdif = snd_hda_spdif_out_of_nid(codec, nid); - - /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */ - if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE)) - set_dig_out_convert(codec, nid, - spdif->ctls & ~AC_DIG1_ENABLE & 0xff, - -1); - snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format); - if (codec->slave_dig_outs) { - const hda_nid_t *d; - for (d = codec->slave_dig_outs; *d; d++) - snd_hda_codec_setup_stream(codec, *d, stream_tag, 0, - format); - } - /* turn on again (if needed) */ - if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE)) - set_dig_out_convert(codec, nid, - spdif->ctls & 0xff, -1); -} - -static void cleanup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid) -{ - snd_hda_codec_cleanup_stream(codec, nid); - if (codec->slave_dig_outs) { - const hda_nid_t *d; - for (d = codec->slave_dig_outs; *d; d++) - snd_hda_codec_cleanup_stream(codec, *d); - } -} - -/** - * snd_hda_bus_reboot_notify - call the reboot notifier of each codec - * @bus: HD-audio bus - */ -void snd_hda_bus_reboot_notify(struct hda_bus *bus) -{ - struct hda_codec *codec; - - if (!bus) - return; - list_for_each_entry(codec, &bus->codec_list, list) { - if (hda_codec_is_power_on(codec) && - codec->patch_ops.reboot_notify) - codec->patch_ops.reboot_notify(codec); - } -} -EXPORT_SYMBOL_HDA(snd_hda_bus_reboot_notify); - -/** - * snd_hda_multi_out_dig_open - open the digital out in the exclusive mode - */ -int snd_hda_multi_out_dig_open(struct hda_codec *codec, - struct hda_multi_out *mout) -{ - mutex_lock(&codec->spdif_mutex); - if (mout->dig_out_used == HDA_DIG_ANALOG_DUP) - /* already opened as analog dup; reset it once */ - cleanup_dig_out_stream(codec, mout->dig_out_nid); - mout->dig_out_used = HDA_DIG_EXCLUSIVE; - mutex_unlock(&codec->spdif_mutex); - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_open); - -/** - * snd_hda_multi_out_dig_prepare - prepare the digital out stream - */ -int snd_hda_multi_out_dig_prepare(struct hda_codec *codec, - struct hda_multi_out *mout, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - mutex_lock(&codec->spdif_mutex); - setup_dig_out_stream(codec, mout->dig_out_nid, stream_tag, format); - mutex_unlock(&codec->spdif_mutex); - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_prepare); - -/** - * snd_hda_multi_out_dig_cleanup - clean-up the digital out stream - */ -int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec, - struct hda_multi_out *mout) -{ - mutex_lock(&codec->spdif_mutex); - cleanup_dig_out_stream(codec, mout->dig_out_nid); - mutex_unlock(&codec->spdif_mutex); - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_cleanup); - -/** - * snd_hda_multi_out_dig_close - release the digital out stream - */ -int snd_hda_multi_out_dig_close(struct hda_codec *codec, - struct hda_multi_out *mout) -{ - mutex_lock(&codec->spdif_mutex); - mout->dig_out_used = 0; - mutex_unlock(&codec->spdif_mutex); - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_close); - -/** - * snd_hda_multi_out_analog_open - open analog outputs - * - * Open analog outputs and set up the hw-constraints. - * If the digital outputs can be opened as slave, open the digital - * outputs, too. - */ -int snd_hda_multi_out_analog_open(struct hda_codec *codec, - struct hda_multi_out *mout, - struct snd_pcm_substream *substream, - struct hda_pcm_stream *hinfo) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - runtime->hw.channels_max = mout->max_channels; - if (mout->dig_out_nid) { - if (!mout->analog_rates) { - mout->analog_rates = hinfo->rates; - mout->analog_formats = hinfo->formats; - mout->analog_maxbps = hinfo->maxbps; - } else { - runtime->hw.rates = mout->analog_rates; - runtime->hw.formats = mout->analog_formats; - hinfo->maxbps = mout->analog_maxbps; - } - if (!mout->spdif_rates) { - snd_hda_query_supported_pcm(codec, mout->dig_out_nid, - &mout->spdif_rates, - &mout->spdif_formats, - &mout->spdif_maxbps); - } - mutex_lock(&codec->spdif_mutex); - if (mout->share_spdif) { - if ((runtime->hw.rates & mout->spdif_rates) && - (runtime->hw.formats & mout->spdif_formats)) { - runtime->hw.rates &= mout->spdif_rates; - runtime->hw.formats &= mout->spdif_formats; - if (mout->spdif_maxbps < hinfo->maxbps) - hinfo->maxbps = mout->spdif_maxbps; - } else { - mout->share_spdif = 0; - /* FIXME: need notify? */ - } - } - mutex_unlock(&codec->spdif_mutex); - } - return snd_pcm_hw_constraint_step(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_CHANNELS, 2); -} -EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_open); - -/** - * snd_hda_multi_out_analog_prepare - Preapre the analog outputs. - * - * Set up the i/o for analog out. - * When the digital out is available, copy the front out to digital out, too. - */ -int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, - struct hda_multi_out *mout, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - const hda_nid_t *nids = mout->dac_nids; - int chs = substream->runtime->channels; - struct hda_spdif_out *spdif = - snd_hda_spdif_out_of_nid(codec, mout->dig_out_nid); - int i; - - mutex_lock(&codec->spdif_mutex); - if (mout->dig_out_nid && mout->share_spdif && - mout->dig_out_used != HDA_DIG_EXCLUSIVE) { - if (chs == 2 && - snd_hda_is_supported_format(codec, mout->dig_out_nid, - format) && - !(spdif->status & IEC958_AES0_NONAUDIO)) { - mout->dig_out_used = HDA_DIG_ANALOG_DUP; - setup_dig_out_stream(codec, mout->dig_out_nid, - stream_tag, format); - } else { - mout->dig_out_used = 0; - cleanup_dig_out_stream(codec, mout->dig_out_nid); - } - } - mutex_unlock(&codec->spdif_mutex); - - /* front */ - snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, - 0, format); - if (!mout->no_share_stream && - mout->hp_nid && mout->hp_nid != nids[HDA_FRONT]) - /* headphone out will just decode front left/right (stereo) */ - snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, - 0, format); - /* extra outputs copied from front */ - for (i = 0; i < ARRAY_SIZE(mout->hp_out_nid); i++) - if (!mout->no_share_stream && mout->hp_out_nid[i]) - snd_hda_codec_setup_stream(codec, - mout->hp_out_nid[i], - stream_tag, 0, format); - for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) - if (!mout->no_share_stream && mout->extra_out_nid[i]) - snd_hda_codec_setup_stream(codec, - mout->extra_out_nid[i], - stream_tag, 0, format); - - /* surrounds */ - for (i = 1; i < mout->num_dacs; i++) { - if (chs >= (i + 1) * 2) /* independent out */ - snd_hda_codec_setup_stream(codec, nids[i], stream_tag, - i * 2, format); - else if (!mout->no_share_stream) /* copy front */ - snd_hda_codec_setup_stream(codec, nids[i], stream_tag, - 0, format); - } - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_prepare); - -/** - * snd_hda_multi_out_analog_cleanup - clean up the setting for analog out - */ -int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, - struct hda_multi_out *mout) -{ - const hda_nid_t *nids = mout->dac_nids; - int i; - - for (i = 0; i < mout->num_dacs; i++) - snd_hda_codec_cleanup_stream(codec, nids[i]); - if (mout->hp_nid) - snd_hda_codec_cleanup_stream(codec, mout->hp_nid); - for (i = 0; i < ARRAY_SIZE(mout->hp_out_nid); i++) - if (mout->hp_out_nid[i]) - snd_hda_codec_cleanup_stream(codec, - mout->hp_out_nid[i]); - for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) - if (mout->extra_out_nid[i]) - snd_hda_codec_cleanup_stream(codec, - mout->extra_out_nid[i]); - mutex_lock(&codec->spdif_mutex); - if (mout->dig_out_nid && mout->dig_out_used == HDA_DIG_ANALOG_DUP) { - cleanup_dig_out_stream(codec, mout->dig_out_nid); - mout->dig_out_used = 0; - } - mutex_unlock(&codec->spdif_mutex); - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_cleanup); - -/* - * Helper for automatic pin configuration - */ - -static int is_in_nid_list(hda_nid_t nid, const hda_nid_t *list) -{ - for (; *list; list++) - if (*list == nid) - return 1; - return 0; -} - - -/* - * Sort an associated group of pins according to their sequence numbers. - */ -static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences, - int num_pins) -{ - int i, j; - short seq; - hda_nid_t nid; - - for (i = 0; i < num_pins; i++) { - for (j = i + 1; j < num_pins; j++) { - if (sequences[i] > sequences[j]) { - seq = sequences[i]; - sequences[i] = sequences[j]; - sequences[j] = seq; - nid = pins[i]; - pins[i] = pins[j]; - pins[j] = nid; - } - } - } -} - - -/* add the found input-pin to the cfg->inputs[] table */ -static void add_auto_cfg_input_pin(struct auto_pin_cfg *cfg, hda_nid_t nid, - int type) -{ - if (cfg->num_inputs < AUTO_CFG_MAX_INS) { - cfg->inputs[cfg->num_inputs].pin = nid; - cfg->inputs[cfg->num_inputs].type = type; - cfg->num_inputs++; - } -} - -/* sort inputs in the order of AUTO_PIN_* type */ -static void sort_autocfg_input_pins(struct auto_pin_cfg *cfg) -{ - int i, j; - - for (i = 0; i < cfg->num_inputs; i++) { - for (j = i + 1; j < cfg->num_inputs; j++) { - if (cfg->inputs[i].type > cfg->inputs[j].type) { - struct auto_pin_cfg_item tmp; - tmp = cfg->inputs[i]; - cfg->inputs[i] = cfg->inputs[j]; - cfg->inputs[j] = tmp; - } - } - } -} - -/* Reorder the surround channels - * ALSA sequence is front/surr/clfe/side - * HDA sequence is: - * 4-ch: front/surr => OK as it is - * 6-ch: front/clfe/surr - * 8-ch: front/clfe/rear/side|fc - */ -static void reorder_outputs(unsigned int nums, hda_nid_t *pins) -{ - hda_nid_t nid; - - switch (nums) { - case 3: - case 4: - nid = pins[1]; - pins[1] = pins[2]; - pins[2] = nid; - break; - } -} - -/* - * Parse all pin widgets and store the useful pin nids to cfg - * - * The number of line-outs or any primary output is stored in line_outs, - * and the corresponding output pins are assigned to line_out_pins[], - * in the order of front, rear, CLFE, side, ... - * - * If more extra outputs (speaker and headphone) are found, the pins are - * assisnged to hp_pins[] and speaker_pins[], respectively. If no line-out jack - * is detected, one of speaker of HP pins is assigned as the primary - * output, i.e. to line_out_pins[0]. So, line_outs is always positive - * if any analog output exists. - * - * The analog input pins are assigned to inputs array. - * The digital input/output pins are assigned to dig_in_pin and dig_out_pin, - * respectively. - */ -int snd_hda_parse_pin_defcfg(struct hda_codec *codec, - struct auto_pin_cfg *cfg, - const hda_nid_t *ignore_nids, - unsigned int cond_flags) -{ - hda_nid_t nid, end_nid; - short seq, assoc_line_out; - short sequences_line_out[ARRAY_SIZE(cfg->line_out_pins)]; - short sequences_speaker[ARRAY_SIZE(cfg->speaker_pins)]; - short sequences_hp[ARRAY_SIZE(cfg->hp_pins)]; - int i; - - memset(cfg, 0, sizeof(*cfg)); - - memset(sequences_line_out, 0, sizeof(sequences_line_out)); - memset(sequences_speaker, 0, sizeof(sequences_speaker)); - memset(sequences_hp, 0, sizeof(sequences_hp)); - assoc_line_out = 0; - - codec->ignore_misc_bit = true; - 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 = get_wcaps_type(wid_caps); - unsigned int def_conf; - short assoc, loc, conn, dev; - - /* read all default configuration for pin complex */ - if (wid_type != AC_WID_PIN) - continue; - /* ignore the given nids (e.g. pc-beep returns error) */ - if (ignore_nids && is_in_nid_list(nid, ignore_nids)) - continue; - - def_conf = snd_hda_codec_get_pincfg(codec, nid); - if (!(get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) & - AC_DEFCFG_MISC_NO_PRESENCE)) - codec->ignore_misc_bit = false; - conn = get_defcfg_connect(def_conf); - if (conn == AC_JACK_PORT_NONE) - continue; - loc = get_defcfg_location(def_conf); - dev = get_defcfg_device(def_conf); - - /* workaround for buggy BIOS setups */ - if (dev == AC_JACK_LINE_OUT) { - if (conn == AC_JACK_PORT_FIXED) - dev = AC_JACK_SPEAKER; - } - - switch (dev) { - case AC_JACK_LINE_OUT: - seq = get_defcfg_sequence(def_conf); - assoc = get_defcfg_association(def_conf); - - if (!(wid_caps & AC_WCAP_STEREO)) - if (!cfg->mono_out_pin) - cfg->mono_out_pin = nid; - if (!assoc) - continue; - if (!assoc_line_out) - assoc_line_out = assoc; - else if (assoc_line_out != assoc) - continue; - if (cfg->line_outs >= ARRAY_SIZE(cfg->line_out_pins)) - continue; - cfg->line_out_pins[cfg->line_outs] = nid; - sequences_line_out[cfg->line_outs] = seq; - cfg->line_outs++; - break; - case AC_JACK_SPEAKER: - seq = get_defcfg_sequence(def_conf); - assoc = get_defcfg_association(def_conf); - if (cfg->speaker_outs >= ARRAY_SIZE(cfg->speaker_pins)) - continue; - cfg->speaker_pins[cfg->speaker_outs] = nid; - sequences_speaker[cfg->speaker_outs] = (assoc << 4) | seq; - cfg->speaker_outs++; - break; - case AC_JACK_HP_OUT: - seq = get_defcfg_sequence(def_conf); - assoc = get_defcfg_association(def_conf); - if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins)) - continue; - cfg->hp_pins[cfg->hp_outs] = nid; - sequences_hp[cfg->hp_outs] = (assoc << 4) | seq; - cfg->hp_outs++; - break; - case AC_JACK_MIC_IN: - add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_MIC); - break; - case AC_JACK_LINE_IN: - add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_LINE_IN); - break; - case AC_JACK_CD: - add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_CD); - break; - case AC_JACK_AUX: - add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_AUX); - break; - case AC_JACK_SPDIF_OUT: - case AC_JACK_DIG_OTHER_OUT: - if (cfg->dig_outs >= ARRAY_SIZE(cfg->dig_out_pins)) - continue; - cfg->dig_out_pins[cfg->dig_outs] = nid; - cfg->dig_out_type[cfg->dig_outs] = - (loc == AC_JACK_LOC_HDMI) ? - HDA_PCM_TYPE_HDMI : HDA_PCM_TYPE_SPDIF; - cfg->dig_outs++; - break; - case AC_JACK_SPDIF_IN: - case AC_JACK_DIG_OTHER_IN: - cfg->dig_in_pin = nid; - if (loc == AC_JACK_LOC_HDMI) - cfg->dig_in_type = HDA_PCM_TYPE_HDMI; - else - cfg->dig_in_type = HDA_PCM_TYPE_SPDIF; - break; - } - } - - /* FIX-UP: - * If no line-out is defined but multiple HPs are found, - * some of them might be the real line-outs. - */ - if (!cfg->line_outs && cfg->hp_outs > 1 && - !(cond_flags & HDA_PINCFG_NO_HP_FIXUP)) { - int i = 0; - while (i < cfg->hp_outs) { - /* The real HPs should have the sequence 0x0f */ - if ((sequences_hp[i] & 0x0f) == 0x0f) { - i++; - continue; - } - /* Move it to the line-out table */ - cfg->line_out_pins[cfg->line_outs] = cfg->hp_pins[i]; - sequences_line_out[cfg->line_outs] = sequences_hp[i]; - cfg->line_outs++; - cfg->hp_outs--; - memmove(cfg->hp_pins + i, cfg->hp_pins + i + 1, - sizeof(cfg->hp_pins[0]) * (cfg->hp_outs - i)); - memmove(sequences_hp + i, sequences_hp + i + 1, - sizeof(sequences_hp[0]) * (cfg->hp_outs - i)); - } - memset(cfg->hp_pins + cfg->hp_outs, 0, - sizeof(hda_nid_t) * (AUTO_CFG_MAX_OUTS - cfg->hp_outs)); - if (!cfg->hp_outs) - cfg->line_out_type = AUTO_PIN_HP_OUT; - - } - - /* sort by sequence */ - sort_pins_by_sequence(cfg->line_out_pins, sequences_line_out, - cfg->line_outs); - sort_pins_by_sequence(cfg->speaker_pins, sequences_speaker, - cfg->speaker_outs); - sort_pins_by_sequence(cfg->hp_pins, sequences_hp, - cfg->hp_outs); - - /* - * FIX-UP: if no line-outs are detected, try to use speaker or HP pin - * as a primary output - */ - if (!cfg->line_outs && - !(cond_flags & HDA_PINCFG_NO_LO_FIXUP)) { - if (cfg->speaker_outs) { - cfg->line_outs = cfg->speaker_outs; - memcpy(cfg->line_out_pins, cfg->speaker_pins, - sizeof(cfg->speaker_pins)); - cfg->speaker_outs = 0; - memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins)); - cfg->line_out_type = AUTO_PIN_SPEAKER_OUT; - } else if (cfg->hp_outs) { - cfg->line_outs = cfg->hp_outs; - memcpy(cfg->line_out_pins, cfg->hp_pins, - sizeof(cfg->hp_pins)); - cfg->hp_outs = 0; - memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); - cfg->line_out_type = AUTO_PIN_HP_OUT; - } - } - - reorder_outputs(cfg->line_outs, cfg->line_out_pins); - reorder_outputs(cfg->hp_outs, cfg->hp_pins); - reorder_outputs(cfg->speaker_outs, cfg->speaker_pins); - - sort_autocfg_input_pins(cfg); - - /* - * debug prints of the parsed results - */ - snd_printd("autoconfig: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x) type:%s\n", - cfg->line_outs, cfg->line_out_pins[0], cfg->line_out_pins[1], - cfg->line_out_pins[2], cfg->line_out_pins[3], - cfg->line_out_pins[4], - cfg->line_out_type == AUTO_PIN_HP_OUT ? "hp" : - (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT ? - "speaker" : "line")); - snd_printd(" speaker_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n", - cfg->speaker_outs, cfg->speaker_pins[0], - cfg->speaker_pins[1], cfg->speaker_pins[2], - cfg->speaker_pins[3], cfg->speaker_pins[4]); - snd_printd(" hp_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n", - cfg->hp_outs, cfg->hp_pins[0], - cfg->hp_pins[1], cfg->hp_pins[2], - cfg->hp_pins[3], cfg->hp_pins[4]); - snd_printd(" mono: mono_out=0x%x\n", cfg->mono_out_pin); - if (cfg->dig_outs) - snd_printd(" dig-out=0x%x/0x%x\n", - cfg->dig_out_pins[0], cfg->dig_out_pins[1]); - snd_printd(" inputs:"); - for (i = 0; i < cfg->num_inputs; i++) { - snd_printd(" %s=0x%x", - hda_get_autocfg_input_label(codec, cfg, i), - cfg->inputs[i].pin); - } - snd_printd("\n"); - if (cfg->dig_in_pin) - snd_printd(" dig-in=0x%x\n", cfg->dig_in_pin); - - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_parse_pin_defcfg); - -int snd_hda_get_input_pin_attr(unsigned int def_conf) -{ - unsigned int loc = get_defcfg_location(def_conf); - unsigned int conn = get_defcfg_connect(def_conf); - if (conn == AC_JACK_PORT_NONE) - return INPUT_PIN_ATTR_UNUSED; - /* Windows may claim the internal mic to be BOTH, too */ - if (conn == AC_JACK_PORT_FIXED || conn == AC_JACK_PORT_BOTH) - return INPUT_PIN_ATTR_INT; - if ((loc & 0x30) == AC_JACK_LOC_INTERNAL) - return INPUT_PIN_ATTR_INT; - if ((loc & 0x30) == AC_JACK_LOC_SEPARATE) - return INPUT_PIN_ATTR_DOCK; - if (loc == AC_JACK_LOC_REAR) - return INPUT_PIN_ATTR_REAR; - if (loc == AC_JACK_LOC_FRONT) - return INPUT_PIN_ATTR_FRONT; - return INPUT_PIN_ATTR_NORMAL; -} -EXPORT_SYMBOL_HDA(snd_hda_get_input_pin_attr); - -/** - * hda_get_input_pin_label - Give a label for the given input pin - * - * When check_location is true, the function checks the pin location - * for mic and line-in pins, and set an appropriate prefix like "Front", - * "Rear", "Internal". - */ - -static const char *hda_get_input_pin_label(struct hda_codec *codec, - hda_nid_t pin, bool check_location) -{ - unsigned int def_conf; - static const char * const mic_names[] = { - "Internal Mic", "Dock Mic", "Mic", "Front Mic", "Rear Mic", - }; - int attr; - - def_conf = snd_hda_codec_get_pincfg(codec, pin); - - switch (get_defcfg_device(def_conf)) { - case AC_JACK_MIC_IN: - if (!check_location) - return "Mic"; - attr = snd_hda_get_input_pin_attr(def_conf); - if (!attr) - return "None"; - return mic_names[attr - 1]; - case AC_JACK_LINE_IN: - if (!check_location) - return "Line"; - attr = snd_hda_get_input_pin_attr(def_conf); - if (!attr) - return "None"; - if (attr == INPUT_PIN_ATTR_DOCK) - return "Dock Line"; - return "Line"; - case AC_JACK_AUX: - return "Aux"; - case AC_JACK_CD: - return "CD"; - case AC_JACK_SPDIF_IN: - return "SPDIF In"; - case AC_JACK_DIG_OTHER_IN: - return "Digital In"; - default: - return "Misc"; - } -} - -/* Check whether the location prefix needs to be added to the label. - * If all mic-jacks are in the same location (e.g. rear panel), we don't - * have to put "Front" prefix to each label. In such a case, returns false. - */ -static int check_mic_location_need(struct hda_codec *codec, - const struct auto_pin_cfg *cfg, - int input) -{ - unsigned int defc; - int i, attr, attr2; - - defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[input].pin); - attr = snd_hda_get_input_pin_attr(defc); - /* for internal or docking mics, we need locations */ - if (attr <= INPUT_PIN_ATTR_NORMAL) - return 1; - - attr = 0; - for (i = 0; i < cfg->num_inputs; i++) { - defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[i].pin); - attr2 = snd_hda_get_input_pin_attr(defc); - if (attr2 >= INPUT_PIN_ATTR_NORMAL) { - if (attr && attr != attr2) - return 1; /* different locations found */ - attr = attr2; - } - } - return 0; -} - -/** - * hda_get_autocfg_input_label - Get a label for the given input - * - * Get a label for the given input pin defined by the autocfg item. - * Unlike hda_get_input_pin_label(), this function checks all inputs - * defined in autocfg and avoids the redundant mic/line prefix as much as - * possible. - */ -const char *hda_get_autocfg_input_label(struct hda_codec *codec, - const struct auto_pin_cfg *cfg, - int input) -{ - int type = cfg->inputs[input].type; - int has_multiple_pins = 0; - - if ((input > 0 && cfg->inputs[input - 1].type == type) || - (input < cfg->num_inputs - 1 && cfg->inputs[input + 1].type == type)) - has_multiple_pins = 1; - if (has_multiple_pins && type == AUTO_PIN_MIC) - has_multiple_pins &= check_mic_location_need(codec, cfg, input); - return hda_get_input_pin_label(codec, cfg->inputs[input].pin, - has_multiple_pins); -} -EXPORT_SYMBOL_HDA(hda_get_autocfg_input_label); - -/* return the position of NID in the list, or -1 if not found */ -static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) -{ - int i; - for (i = 0; i < nums; i++) - if (list[i] == nid) - return i; - return -1; -} - -/* get a unique suffix or an index number */ -static const char *check_output_sfx(hda_nid_t nid, const hda_nid_t *pins, - int num_pins, int *indexp) -{ - static const char * const channel_sfx[] = { - " Front", " Surround", " CLFE", " Side" - }; - int i; - - i = find_idx_in_nid_list(nid, pins, num_pins); - if (i < 0) - return NULL; - if (num_pins == 1) - return ""; - if (num_pins > ARRAY_SIZE(channel_sfx)) { - if (indexp) - *indexp = i; - return ""; - } - return channel_sfx[i]; -} - -static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid, - const struct auto_pin_cfg *cfg, - const char *name, char *label, int maxlen, - int *indexp) -{ - unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); - int attr = snd_hda_get_input_pin_attr(def_conf); - const char *pfx = "", *sfx = ""; - - /* handle as a speaker if it's a fixed line-out */ - if (!strcmp(name, "Line Out") && attr == INPUT_PIN_ATTR_INT) - name = "Speaker"; - /* check the location */ - switch (attr) { - case INPUT_PIN_ATTR_DOCK: - pfx = "Dock "; - break; - case INPUT_PIN_ATTR_FRONT: - pfx = "Front "; - break; - } - if (cfg) { - /* try to give a unique suffix if needed */ - sfx = check_output_sfx(nid, cfg->line_out_pins, cfg->line_outs, - indexp); - if (!sfx) - sfx = check_output_sfx(nid, cfg->speaker_pins, cfg->speaker_outs, - indexp); - if (!sfx) { - /* don't add channel suffix for Headphone controls */ - int idx = find_idx_in_nid_list(nid, cfg->hp_pins, - cfg->hp_outs); - if (idx >= 0) - *indexp = idx; - sfx = ""; - } - } - snprintf(label, maxlen, "%s%s%s", pfx, name, sfx); - return 1; -} - -/** - * snd_hda_get_pin_label - Get a label for the given I/O pin - * - * Get a label for the given pin. This function works for both input and - * output pins. When @cfg is given as non-NULL, the function tries to get - * an optimized label using hda_get_autocfg_input_label(). - * - * This function tries to give a unique label string for the pin as much as - * possible. For example, when the multiple line-outs are present, it adds - * the channel suffix like "Front", "Surround", etc (only when @cfg is given). - * If no unique name with a suffix is available and @indexp is non-NULL, the - * index number is stored in the pointer. - */ -int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, - const struct auto_pin_cfg *cfg, - char *label, int maxlen, int *indexp) -{ - unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); - const char *name = NULL; - int i; - - if (indexp) - *indexp = 0; - if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) - return 0; - - switch (get_defcfg_device(def_conf)) { - case AC_JACK_LINE_OUT: - return fill_audio_out_name(codec, nid, cfg, "Line Out", - label, maxlen, indexp); - case AC_JACK_SPEAKER: - return fill_audio_out_name(codec, nid, cfg, "Speaker", - label, maxlen, indexp); - case AC_JACK_HP_OUT: - return fill_audio_out_name(codec, nid, cfg, "Headphone", - label, maxlen, indexp); - case AC_JACK_SPDIF_OUT: - case AC_JACK_DIG_OTHER_OUT: - if (get_defcfg_location(def_conf) == AC_JACK_LOC_HDMI) - name = "HDMI"; - else - name = "SPDIF"; - if (cfg && indexp) { - i = find_idx_in_nid_list(nid, cfg->dig_out_pins, - cfg->dig_outs); - if (i >= 0) - *indexp = i; - } - break; - default: - if (cfg) { - for (i = 0; i < cfg->num_inputs; i++) { - if (cfg->inputs[i].pin != nid) - continue; - name = hda_get_autocfg_input_label(codec, cfg, i); - if (name) - break; - } - } - if (!name) - name = hda_get_input_pin_label(codec, nid, true); - break; - } - if (!name) - return 0; - strlcpy(label, name, maxlen); - return 1; -} -EXPORT_SYMBOL_HDA(snd_hda_get_pin_label); - -/** - * snd_hda_add_imux_item - Add an item to input_mux - * - * When the same label is used already in the existing items, the number - * suffix is appended to the label. This label index number is stored - * to type_idx when non-NULL pointer is given. - */ -int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label, - int index, int *type_idx) -{ - int i, label_idx = 0; - if (imux->num_items >= HDA_MAX_NUM_INPUTS) { - snd_printd(KERN_ERR "hda_codec: Too many imux items!\n"); - return -EINVAL; - } - for (i = 0; i < imux->num_items; i++) { - if (!strncmp(label, imux->items[i].label, strlen(label))) - label_idx++; - } - if (type_idx) - *type_idx = label_idx; - if (label_idx > 0) - snprintf(imux->items[imux->num_items].label, - sizeof(imux->items[imux->num_items].label), - "%s %d", label, label_idx); - else - strlcpy(imux->items[imux->num_items].label, label, - sizeof(imux->items[imux->num_items].label)); - imux->items[imux->num_items].index = index; - imux->num_items++; - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_add_imux_item); - - -#ifdef CONFIG_PM -/* - * power management - */ - -/** - * snd_hda_suspend - suspend the codecs - * @bus: the HDA bus - * - * Returns 0 if successful. - */ -int snd_hda_suspend(struct hda_bus *bus) -{ - struct hda_codec *codec; - - list_for_each_entry(codec, &bus->codec_list, list) { - if (hda_codec_is_power_on(codec)) - hda_call_codec_suspend(codec); - if (codec->patch_ops.post_suspend) - codec->patch_ops.post_suspend(codec); - } - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_suspend); - -/** - * snd_hda_resume - resume the codecs - * @bus: the HDA bus - * - * Returns 0 if successful. - * - * This function is defined only when POWER_SAVE isn't set. - * In the power-save mode, the codec is resumed dynamically. - */ -int snd_hda_resume(struct hda_bus *bus) -{ - struct hda_codec *codec; - - list_for_each_entry(codec, &bus->codec_list, list) { - if (codec->patch_ops.pre_resume) - codec->patch_ops.pre_resume(codec); - if (snd_hda_codec_needs_resume(codec)) - hda_call_codec_resume(codec); - } - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_resume); -#endif /* CONFIG_PM */ - -/* - * generic arrays - */ - -/** - * snd_array_new - get a new element from the given array - * @array: the array object - * - * Get a new element from the given array. If it exceeds the - * pre-allocated array size, re-allocate the array. - * - * Returns NULL if allocation failed. - */ -void *snd_array_new(struct snd_array *array) -{ - if (array->used >= array->alloced) { - int num = array->alloced + array->alloc_align; - int size = (num + 1) * array->elem_size; - int oldsize = array->alloced * array->elem_size; - void *nlist; - if (snd_BUG_ON(num >= 4096)) - return NULL; - nlist = krealloc(array->list, size, GFP_KERNEL); - if (!nlist) - return NULL; - memset(nlist + oldsize, 0, size - oldsize); - array->list = nlist; - array->alloced = num; - } - return snd_array_elem(array, array->used++); -} -EXPORT_SYMBOL_HDA(snd_array_new); - -/** - * snd_array_free - free the given array elements - * @array: the array object - */ -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); - -/** - * snd_print_pcm_bits - Print the supported PCM fmt bits to the string buffer - * @pcm: PCM caps bits - * @buf: the string buffer to write - * @buflen: the max buffer length - * - * used by hda_proc.c and hda_eld.c - */ -void snd_print_pcm_bits(int pcm, char *buf, int buflen) -{ - static unsigned int bits[] = { 8, 16, 20, 24, 32 }; - 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/ANDROID_3.4.5/sound/pci/hda/hda_codec.h b/ANDROID_3.4.5/sound/pci/hda/hda_codec.h deleted file mode 100644 index 56b4f74c..00000000 --- a/ANDROID_3.4.5/sound/pci/hda/hda_codec.h +++ /dev/null @@ -1,1084 +0,0 @@ -/* - * Universal Interface for Intel High Definition Audio Codec - * - * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> - * - * 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. - */ - -#ifndef __SOUND_HDA_CODEC_H -#define __SOUND_HDA_CODEC_H - -#include <sound/info.h> -#include <sound/control.h> -#include <sound/pcm.h> -#include <sound/hwdep.h> - -/* - * nodes - */ -#define AC_NODE_ROOT 0x00 - -/* - * function group types - */ -enum { - AC_GRP_AUDIO_FUNCTION = 0x01, - AC_GRP_MODEM_FUNCTION = 0x02, -}; - -/* - * widget types - */ -enum { - AC_WID_AUD_OUT, /* Audio Out */ - AC_WID_AUD_IN, /* Audio In */ - AC_WID_AUD_MIX, /* Audio Mixer */ - AC_WID_AUD_SEL, /* Audio Selector */ - AC_WID_PIN, /* Pin Complex */ - AC_WID_POWER, /* Power */ - AC_WID_VOL_KNB, /* Volume Knob */ - AC_WID_BEEP, /* Beep Generator */ - AC_WID_VENDOR = 0x0f /* Vendor specific */ -}; - -/* - * GET verbs - */ -#define AC_VERB_GET_STREAM_FORMAT 0x0a00 -#define AC_VERB_GET_AMP_GAIN_MUTE 0x0b00 -#define AC_VERB_GET_PROC_COEF 0x0c00 -#define AC_VERB_GET_COEF_INDEX 0x0d00 -#define AC_VERB_PARAMETERS 0x0f00 -#define AC_VERB_GET_CONNECT_SEL 0x0f01 -#define AC_VERB_GET_CONNECT_LIST 0x0f02 -#define AC_VERB_GET_PROC_STATE 0x0f03 -#define AC_VERB_GET_SDI_SELECT 0x0f04 -#define AC_VERB_GET_POWER_STATE 0x0f05 -#define AC_VERB_GET_CONV 0x0f06 -#define AC_VERB_GET_PIN_WIDGET_CONTROL 0x0f07 -#define AC_VERB_GET_UNSOLICITED_RESPONSE 0x0f08 -#define AC_VERB_GET_PIN_SENSE 0x0f09 -#define AC_VERB_GET_BEEP_CONTROL 0x0f0a -#define AC_VERB_GET_EAPD_BTLENABLE 0x0f0c -#define AC_VERB_GET_DIGI_CONVERT_1 0x0f0d -#define AC_VERB_GET_DIGI_CONVERT_2 0x0f0e /* unused */ -#define AC_VERB_GET_VOLUME_KNOB_CONTROL 0x0f0f -/* f10-f1a: GPIO */ -#define AC_VERB_GET_GPIO_DATA 0x0f15 -#define AC_VERB_GET_GPIO_MASK 0x0f16 -#define AC_VERB_GET_GPIO_DIRECTION 0x0f17 -#define AC_VERB_GET_GPIO_WAKE_MASK 0x0f18 -#define AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK 0x0f19 -#define AC_VERB_GET_GPIO_STICKY_MASK 0x0f1a -#define AC_VERB_GET_CONFIG_DEFAULT 0x0f1c -/* f20: AFG/MFG */ -#define AC_VERB_GET_SUBSYSTEM_ID 0x0f20 -#define AC_VERB_GET_CVT_CHAN_COUNT 0x0f2d -#define AC_VERB_GET_HDMI_DIP_SIZE 0x0f2e -#define AC_VERB_GET_HDMI_ELDD 0x0f2f -#define AC_VERB_GET_HDMI_DIP_INDEX 0x0f30 -#define AC_VERB_GET_HDMI_DIP_DATA 0x0f31 -#define AC_VERB_GET_HDMI_DIP_XMIT 0x0f32 -#define AC_VERB_GET_HDMI_CP_CTRL 0x0f33 -#define AC_VERB_GET_HDMI_CHAN_SLOT 0x0f34 - -/* - * SET verbs - */ -#define AC_VERB_SET_STREAM_FORMAT 0x200 -#define AC_VERB_SET_AMP_GAIN_MUTE 0x300 -#define AC_VERB_SET_PROC_COEF 0x400 -#define AC_VERB_SET_COEF_INDEX 0x500 -#define AC_VERB_SET_CONNECT_SEL 0x701 -#define AC_VERB_SET_PROC_STATE 0x703 -#define AC_VERB_SET_SDI_SELECT 0x704 -#define AC_VERB_SET_POWER_STATE 0x705 -#define AC_VERB_SET_CHANNEL_STREAMID 0x706 -#define AC_VERB_SET_PIN_WIDGET_CONTROL 0x707 -#define AC_VERB_SET_UNSOLICITED_ENABLE 0x708 -#define AC_VERB_SET_PIN_SENSE 0x709 -#define AC_VERB_SET_BEEP_CONTROL 0x70a -#define AC_VERB_SET_EAPD_BTLENABLE 0x70c -#define AC_VERB_SET_DIGI_CONVERT_1 0x70d -#define AC_VERB_SET_DIGI_CONVERT_2 0x70e -#define AC_VERB_SET_VOLUME_KNOB_CONTROL 0x70f -#define AC_VERB_SET_GPIO_DATA 0x715 -#define AC_VERB_SET_GPIO_MASK 0x716 -#define AC_VERB_SET_GPIO_DIRECTION 0x717 -#define AC_VERB_SET_GPIO_WAKE_MASK 0x718 -#define AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK 0x719 -#define AC_VERB_SET_GPIO_STICKY_MASK 0x71a -#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 0x71c -#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_1 0x71d -#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_2 0x71e -#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_3 0x71f -#define AC_VERB_SET_EAPD 0x788 -#define AC_VERB_SET_CODEC_RESET 0x7ff -#define AC_VERB_SET_CVT_CHAN_COUNT 0x72d -#define AC_VERB_SET_HDMI_DIP_INDEX 0x730 -#define AC_VERB_SET_HDMI_DIP_DATA 0x731 -#define AC_VERB_SET_HDMI_DIP_XMIT 0x732 -#define AC_VERB_SET_HDMI_CP_CTRL 0x733 -#define AC_VERB_SET_HDMI_CHAN_SLOT 0x734 - -/* - * Parameter IDs - */ -#define AC_PAR_VENDOR_ID 0x00 -#define AC_PAR_SUBSYSTEM_ID 0x01 -#define AC_PAR_REV_ID 0x02 -#define AC_PAR_NODE_COUNT 0x04 -#define AC_PAR_FUNCTION_TYPE 0x05 -#define AC_PAR_AUDIO_FG_CAP 0x08 -#define AC_PAR_AUDIO_WIDGET_CAP 0x09 -#define AC_PAR_PCM 0x0a -#define AC_PAR_STREAM 0x0b -#define AC_PAR_PIN_CAP 0x0c -#define AC_PAR_AMP_IN_CAP 0x0d -#define AC_PAR_CONNLIST_LEN 0x0e -#define AC_PAR_POWER_STATE 0x0f -#define AC_PAR_PROC_CAP 0x10 -#define AC_PAR_GPIO_CAP 0x11 -#define AC_PAR_AMP_OUT_CAP 0x12 -#define AC_PAR_VOL_KNB_CAP 0x13 -#define AC_PAR_HDMI_LPCM_CAP 0x20 - -/* - * AC_VERB_PARAMETERS results (32bit) - */ - -/* Function Group Type */ -#define AC_FGT_TYPE (0xff<<0) -#define AC_FGT_TYPE_SHIFT 0 -#define AC_FGT_UNSOL_CAP (1<<8) - -/* Audio Function Group Capabilities */ -#define AC_AFG_OUT_DELAY (0xf<<0) -#define AC_AFG_IN_DELAY (0xf<<8) -#define AC_AFG_BEEP_GEN (1<<16) - -/* Audio Widget Capabilities */ -#define AC_WCAP_STEREO (1<<0) /* stereo I/O */ -#define AC_WCAP_IN_AMP (1<<1) /* AMP-in present */ -#define AC_WCAP_OUT_AMP (1<<2) /* AMP-out present */ -#define AC_WCAP_AMP_OVRD (1<<3) /* AMP-parameter override */ -#define AC_WCAP_FORMAT_OVRD (1<<4) /* format override */ -#define AC_WCAP_STRIPE (1<<5) /* stripe */ -#define AC_WCAP_PROC_WID (1<<6) /* Proc Widget */ -#define AC_WCAP_UNSOL_CAP (1<<7) /* Unsol capable */ -#define AC_WCAP_CONN_LIST (1<<8) /* connection list */ -#define AC_WCAP_DIGITAL (1<<9) /* digital I/O */ -#define AC_WCAP_POWER (1<<10) /* power control */ -#define AC_WCAP_LR_SWAP (1<<11) /* L/R swap */ -#define AC_WCAP_CP_CAPS (1<<12) /* content protection */ -#define AC_WCAP_CHAN_CNT_EXT (7<<13) /* channel count ext */ -#define AC_WCAP_DELAY (0xf<<16) -#define AC_WCAP_DELAY_SHIFT 16 -#define AC_WCAP_TYPE (0xf<<20) -#define AC_WCAP_TYPE_SHIFT 20 - -/* supported PCM rates and bits */ -#define AC_SUPPCM_RATES (0xfff << 0) -#define AC_SUPPCM_BITS_8 (1<<16) -#define AC_SUPPCM_BITS_16 (1<<17) -#define AC_SUPPCM_BITS_20 (1<<18) -#define AC_SUPPCM_BITS_24 (1<<19) -#define AC_SUPPCM_BITS_32 (1<<20) - -/* supported PCM stream format */ -#define AC_SUPFMT_PCM (1<<0) -#define AC_SUPFMT_FLOAT32 (1<<1) -#define AC_SUPFMT_AC3 (1<<2) - -/* GP I/O count */ -#define AC_GPIO_IO_COUNT (0xff<<0) -#define AC_GPIO_O_COUNT (0xff<<8) -#define AC_GPIO_O_COUNT_SHIFT 8 -#define AC_GPIO_I_COUNT (0xff<<16) -#define AC_GPIO_I_COUNT_SHIFT 16 -#define AC_GPIO_UNSOLICITED (1<<30) -#define AC_GPIO_WAKE (1<<31) - -/* Converter stream, channel */ -#define AC_CONV_CHANNEL (0xf<<0) -#define AC_CONV_STREAM (0xf<<4) -#define AC_CONV_STREAM_SHIFT 4 - -/* Input converter SDI select */ -#define AC_SDI_SELECT (0xf<<0) - -/* stream format id */ -#define AC_FMT_CHAN_SHIFT 0 -#define AC_FMT_CHAN_MASK (0x0f << 0) -#define AC_FMT_BITS_SHIFT 4 -#define AC_FMT_BITS_MASK (7 << 4) -#define AC_FMT_BITS_8 (0 << 4) -#define AC_FMT_BITS_16 (1 << 4) -#define AC_FMT_BITS_20 (2 << 4) -#define AC_FMT_BITS_24 (3 << 4) -#define AC_FMT_BITS_32 (4 << 4) -#define AC_FMT_DIV_SHIFT 8 -#define AC_FMT_DIV_MASK (7 << 8) -#define AC_FMT_MULT_SHIFT 11 -#define AC_FMT_MULT_MASK (7 << 11) -#define AC_FMT_BASE_SHIFT 14 -#define AC_FMT_BASE_48K (0 << 14) -#define AC_FMT_BASE_44K (1 << 14) -#define AC_FMT_TYPE_SHIFT 15 -#define AC_FMT_TYPE_PCM (0 << 15) -#define AC_FMT_TYPE_NON_PCM (1 << 15) - -/* Unsolicited response control */ -#define AC_UNSOL_TAG (0x3f<<0) -#define AC_UNSOL_ENABLED (1<<7) -#define AC_USRSP_EN AC_UNSOL_ENABLED - -/* Unsolicited responses */ -#define AC_UNSOL_RES_TAG (0x3f<<26) -#define AC_UNSOL_RES_TAG_SHIFT 26 -#define AC_UNSOL_RES_SUBTAG (0x1f<<21) -#define AC_UNSOL_RES_SUBTAG_SHIFT 21 -#define AC_UNSOL_RES_ELDV (1<<1) /* ELD Data valid (for HDMI) */ -#define AC_UNSOL_RES_PD (1<<0) /* pinsense detect */ -#define AC_UNSOL_RES_CP_STATE (1<<1) /* content protection */ -#define AC_UNSOL_RES_CP_READY (1<<0) /* content protection */ - -/* Pin widget capabilies */ -#define AC_PINCAP_IMP_SENSE (1<<0) /* impedance sense capable */ -#define AC_PINCAP_TRIG_REQ (1<<1) /* trigger required */ -#define AC_PINCAP_PRES_DETECT (1<<2) /* presence detect capable */ -#define AC_PINCAP_HP_DRV (1<<3) /* headphone drive capable */ -#define AC_PINCAP_OUT (1<<4) /* output capable */ -#define AC_PINCAP_IN (1<<5) /* input capable */ -#define AC_PINCAP_BALANCE (1<<6) /* balanced I/O capable */ -/* Note: This LR_SWAP pincap is defined in the Realtek ALC883 specification, - * but is marked reserved in the Intel HDA specification. - */ -#define AC_PINCAP_LR_SWAP (1<<7) /* L/R swap */ -/* Note: The same bit as LR_SWAP is newly defined as HDMI capability - * in HD-audio specification - */ -#define AC_PINCAP_HDMI (1<<7) /* HDMI pin */ -#define AC_PINCAP_DP (1<<24) /* DisplayPort pin, can - * coexist with AC_PINCAP_HDMI - */ -#define AC_PINCAP_VREF (0x37<<8) -#define AC_PINCAP_VREF_SHIFT 8 -#define AC_PINCAP_EAPD (1<<16) /* EAPD capable */ -#define AC_PINCAP_HBR (1<<27) /* High Bit Rate */ -/* Vref status (used in pin cap) */ -#define AC_PINCAP_VREF_HIZ (1<<0) /* Hi-Z */ -#define AC_PINCAP_VREF_50 (1<<1) /* 50% */ -#define AC_PINCAP_VREF_GRD (1<<2) /* ground */ -#define AC_PINCAP_VREF_80 (1<<4) /* 80% */ -#define AC_PINCAP_VREF_100 (1<<5) /* 100% */ - -/* Amplifier capabilities */ -#define AC_AMPCAP_OFFSET (0x7f<<0) /* 0dB offset */ -#define AC_AMPCAP_OFFSET_SHIFT 0 -#define AC_AMPCAP_NUM_STEPS (0x7f<<8) /* number of steps */ -#define AC_AMPCAP_NUM_STEPS_SHIFT 8 -#define AC_AMPCAP_STEP_SIZE (0x7f<<16) /* step size 0-32dB - * in 0.25dB - */ -#define AC_AMPCAP_STEP_SIZE_SHIFT 16 -#define AC_AMPCAP_MUTE (1<<31) /* mute capable */ -#define AC_AMPCAP_MUTE_SHIFT 31 - -/* driver-specific amp-caps: using bits 24-30 */ -#define AC_AMPCAP_MIN_MUTE (1 << 30) /* min-volume = mute */ - -/* Connection list */ -#define AC_CLIST_LENGTH (0x7f<<0) -#define AC_CLIST_LONG (1<<7) - -/* Supported power status */ -#define AC_PWRST_D0SUP (1<<0) -#define AC_PWRST_D1SUP (1<<1) -#define AC_PWRST_D2SUP (1<<2) -#define AC_PWRST_D3SUP (1<<3) -#define AC_PWRST_D3COLDSUP (1<<4) -#define AC_PWRST_S3D3COLDSUP (1<<29) -#define AC_PWRST_CLKSTOP (1<<30) -#define AC_PWRST_EPSS (1U<<31) - -/* Power state values */ -#define AC_PWRST_SETTING (0xf<<0) -#define AC_PWRST_ACTUAL (0xf<<4) -#define AC_PWRST_ACTUAL_SHIFT 4 -#define AC_PWRST_D0 0x00 -#define AC_PWRST_D1 0x01 -#define AC_PWRST_D2 0x02 -#define AC_PWRST_D3 0x03 - -/* Processing capabilies */ -#define AC_PCAP_BENIGN (1<<0) -#define AC_PCAP_NUM_COEF (0xff<<8) -#define AC_PCAP_NUM_COEF_SHIFT 8 - -/* Volume knobs capabilities */ -#define AC_KNBCAP_NUM_STEPS (0x7f<<0) -#define AC_KNBCAP_DELTA (1<<7) - -/* HDMI LPCM capabilities */ -#define AC_LPCMCAP_48K_CP_CHNS (0x0f<<0) /* max channels w/ CP-on */ -#define AC_LPCMCAP_48K_NO_CHNS (0x0f<<4) /* max channels w/o CP-on */ -#define AC_LPCMCAP_48K_20BIT (1<<8) /* 20b bitrate supported */ -#define AC_LPCMCAP_48K_24BIT (1<<9) /* 24b bitrate supported */ -#define AC_LPCMCAP_96K_CP_CHNS (0x0f<<10) /* max channels w/ CP-on */ -#define AC_LPCMCAP_96K_NO_CHNS (0x0f<<14) /* max channels w/o CP-on */ -#define AC_LPCMCAP_96K_20BIT (1<<18) /* 20b bitrate supported */ -#define AC_LPCMCAP_96K_24BIT (1<<19) /* 24b bitrate supported */ -#define AC_LPCMCAP_192K_CP_CHNS (0x0f<<20) /* max channels w/ CP-on */ -#define AC_LPCMCAP_192K_NO_CHNS (0x0f<<24) /* max channels w/o CP-on */ -#define AC_LPCMCAP_192K_20BIT (1<<28) /* 20b bitrate supported */ -#define AC_LPCMCAP_192K_24BIT (1<<29) /* 24b bitrate supported */ -#define AC_LPCMCAP_44K (1<<30) /* 44.1kHz support */ -#define AC_LPCMCAP_44K_MS (1<<31) /* 44.1kHz-multiplies support */ - -/* - * Control Parameters - */ - -/* Amp gain/mute */ -#define AC_AMP_MUTE (1<<7) -#define AC_AMP_GAIN (0x7f) -#define AC_AMP_GET_INDEX (0xf<<0) - -#define AC_AMP_GET_LEFT (1<<13) -#define AC_AMP_GET_RIGHT (0<<13) -#define AC_AMP_GET_OUTPUT (1<<15) -#define AC_AMP_GET_INPUT (0<<15) - -#define AC_AMP_SET_INDEX (0xf<<8) -#define AC_AMP_SET_INDEX_SHIFT 8 -#define AC_AMP_SET_RIGHT (1<<12) -#define AC_AMP_SET_LEFT (1<<13) -#define AC_AMP_SET_INPUT (1<<14) -#define AC_AMP_SET_OUTPUT (1<<15) - -/* DIGITAL1 bits */ -#define AC_DIG1_ENABLE (1<<0) -#define AC_DIG1_V (1<<1) -#define AC_DIG1_VCFG (1<<2) -#define AC_DIG1_EMPHASIS (1<<3) -#define AC_DIG1_COPYRIGHT (1<<4) -#define AC_DIG1_NONAUDIO (1<<5) -#define AC_DIG1_PROFESSIONAL (1<<6) -#define AC_DIG1_LEVEL (1<<7) - -/* DIGITAL2 bits */ -#define AC_DIG2_CC (0x7f<<0) - -/* Pin widget control - 8bit */ -#define AC_PINCTL_EPT (0x3<<0) -#define AC_PINCTL_EPT_NATIVE 0 -#define AC_PINCTL_EPT_HBR 3 -#define AC_PINCTL_VREFEN (0x7<<0) -#define AC_PINCTL_VREF_HIZ 0 /* Hi-Z */ -#define AC_PINCTL_VREF_50 1 /* 50% */ -#define AC_PINCTL_VREF_GRD 2 /* ground */ -#define AC_PINCTL_VREF_80 4 /* 80% */ -#define AC_PINCTL_VREF_100 5 /* 100% */ -#define AC_PINCTL_IN_EN (1<<5) -#define AC_PINCTL_OUT_EN (1<<6) -#define AC_PINCTL_HP_EN (1<<7) - -/* Pin sense - 32bit */ -#define AC_PINSENSE_IMPEDANCE_MASK (0x7fffffff) -#define AC_PINSENSE_PRESENCE (1<<31) -#define AC_PINSENSE_ELDV (1<<30) /* ELD valid (HDMI) */ - -/* EAPD/BTL enable - 32bit */ -#define AC_EAPDBTL_BALANCED (1<<0) -#define AC_EAPDBTL_EAPD (1<<1) -#define AC_EAPDBTL_LR_SWAP (1<<2) - -/* HDMI ELD data */ -#define AC_ELDD_ELD_VALID (1<<31) -#define AC_ELDD_ELD_DATA 0xff - -/* HDMI DIP size */ -#define AC_DIPSIZE_ELD_BUF (1<<3) /* ELD buf size of packet size */ -#define AC_DIPSIZE_PACK_IDX (0x07<<0) /* packet index */ - -/* HDMI DIP index */ -#define AC_DIPIDX_PACK_IDX (0x07<<5) /* packet idnex */ -#define AC_DIPIDX_BYTE_IDX (0x1f<<0) /* byte index */ - -/* HDMI DIP xmit (transmit) control */ -#define AC_DIPXMIT_MASK (0x3<<6) -#define AC_DIPXMIT_DISABLE (0x0<<6) /* disable xmit */ -#define AC_DIPXMIT_ONCE (0x2<<6) /* xmit once then disable */ -#define AC_DIPXMIT_BEST (0x3<<6) /* best effort */ - -/* HDMI content protection (CP) control */ -#define AC_CPCTRL_CES (1<<9) /* current encryption state */ -#define AC_CPCTRL_READY (1<<8) /* ready bit */ -#define AC_CPCTRL_SUBTAG (0x1f<<3) /* subtag for unsol-resp */ -#define AC_CPCTRL_STATE (3<<0) /* current CP request state */ - -/* Converter channel <-> HDMI slot mapping */ -#define AC_CVTMAP_HDMI_SLOT (0xf<<0) /* HDMI slot number */ -#define AC_CVTMAP_CHAN (0xf<<4) /* converter channel number */ - -/* configuration default - 32bit */ -#define AC_DEFCFG_SEQUENCE (0xf<<0) -#define AC_DEFCFG_DEF_ASSOC (0xf<<4) -#define AC_DEFCFG_ASSOC_SHIFT 4 -#define AC_DEFCFG_MISC (0xf<<8) -#define AC_DEFCFG_MISC_SHIFT 8 -#define AC_DEFCFG_MISC_NO_PRESENCE (1<<0) -#define AC_DEFCFG_COLOR (0xf<<12) -#define AC_DEFCFG_COLOR_SHIFT 12 -#define AC_DEFCFG_CONN_TYPE (0xf<<16) -#define AC_DEFCFG_CONN_TYPE_SHIFT 16 -#define AC_DEFCFG_DEVICE (0xf<<20) -#define AC_DEFCFG_DEVICE_SHIFT 20 -#define AC_DEFCFG_LOCATION (0x3f<<24) -#define AC_DEFCFG_LOCATION_SHIFT 24 -#define AC_DEFCFG_PORT_CONN (0x3<<30) -#define AC_DEFCFG_PORT_CONN_SHIFT 30 - -/* device device types (0x0-0xf) */ -enum { - AC_JACK_LINE_OUT, - AC_JACK_SPEAKER, - AC_JACK_HP_OUT, - AC_JACK_CD, - AC_JACK_SPDIF_OUT, - AC_JACK_DIG_OTHER_OUT, - AC_JACK_MODEM_LINE_SIDE, - AC_JACK_MODEM_HAND_SIDE, - AC_JACK_LINE_IN, - AC_JACK_AUX, - AC_JACK_MIC_IN, - AC_JACK_TELEPHONY, - AC_JACK_SPDIF_IN, - AC_JACK_DIG_OTHER_IN, - AC_JACK_OTHER = 0xf, -}; - -/* jack connection types (0x0-0xf) */ -enum { - AC_JACK_CONN_UNKNOWN, - AC_JACK_CONN_1_8, - AC_JACK_CONN_1_4, - AC_JACK_CONN_ATAPI, - AC_JACK_CONN_RCA, - AC_JACK_CONN_OPTICAL, - AC_JACK_CONN_OTHER_DIGITAL, - AC_JACK_CONN_OTHER_ANALOG, - AC_JACK_CONN_DIN, - AC_JACK_CONN_XLR, - AC_JACK_CONN_RJ11, - AC_JACK_CONN_COMB, - AC_JACK_CONN_OTHER = 0xf, -}; - -/* jack colors (0x0-0xf) */ -enum { - AC_JACK_COLOR_UNKNOWN, - AC_JACK_COLOR_BLACK, - AC_JACK_COLOR_GREY, - AC_JACK_COLOR_BLUE, - AC_JACK_COLOR_GREEN, - AC_JACK_COLOR_RED, - AC_JACK_COLOR_ORANGE, - AC_JACK_COLOR_YELLOW, - AC_JACK_COLOR_PURPLE, - AC_JACK_COLOR_PINK, - AC_JACK_COLOR_WHITE = 0xe, - AC_JACK_COLOR_OTHER, -}; - -/* Jack location (0x0-0x3f) */ -/* common case */ -enum { - AC_JACK_LOC_NONE, - AC_JACK_LOC_REAR, - AC_JACK_LOC_FRONT, - AC_JACK_LOC_LEFT, - AC_JACK_LOC_RIGHT, - AC_JACK_LOC_TOP, - AC_JACK_LOC_BOTTOM, -}; -/* bits 4-5 */ -enum { - AC_JACK_LOC_EXTERNAL = 0x00, - AC_JACK_LOC_INTERNAL = 0x10, - AC_JACK_LOC_SEPARATE = 0x20, - AC_JACK_LOC_OTHER = 0x30, -}; -enum { - /* external on primary chasis */ - AC_JACK_LOC_REAR_PANEL = 0x07, - AC_JACK_LOC_DRIVE_BAY, - /* internal */ - AC_JACK_LOC_RISER = 0x17, - AC_JACK_LOC_HDMI, - AC_JACK_LOC_ATAPI, - /* others */ - AC_JACK_LOC_MOBILE_IN = 0x37, - AC_JACK_LOC_MOBILE_OUT, -}; - -/* Port connectivity (0-3) */ -enum { - AC_JACK_PORT_COMPLEX, - AC_JACK_PORT_NONE, - AC_JACK_PORT_FIXED, - AC_JACK_PORT_BOTH, -}; - -/* max. connections to a widget */ -#define HDA_MAX_CONNECTIONS 32 - -/* max. codec address */ -#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 - */ - -struct hda_bus; -struct hda_beep; -struct hda_codec; -struct hda_pcm; -struct hda_pcm_stream; -struct hda_bus_unsolicited; - -/* NID type */ -typedef u16 hda_nid_t; - -/* bus operators */ -struct hda_bus_ops { - /* send a single command */ - int (*command)(struct hda_bus *bus, unsigned int cmd); - /* get a response from the last command */ - unsigned int (*get_response)(struct hda_bus *bus, unsigned int addr); - /* 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); - /* reset bus for retry verb */ - void (*bus_reset)(struct hda_bus *bus); -#ifdef CONFIG_SND_HDA_POWER_SAVE - /* notify power-up/down from codec to controller */ - void (*pm_notify)(struct hda_bus *bus); -#endif -}; - -/* template to pass to the bus constructor */ -struct hda_bus_template { - void *private_data; - struct pci_dev *pci; - const char *modelname; - int *power_save; - struct hda_bus_ops ops; -}; - -/* - * codec bus - * - * each controller needs to creata a hda_bus to assign the accessor. - * A hda_bus contains several codecs in the list codec_list. - */ -struct hda_bus { - struct snd_card *card; - - /* copied from template */ - void *private_data; - struct pci_dev *pci; - const char *modelname; - int *power_save; - struct hda_bus_ops ops; - - /* codec linked list */ - struct list_head codec_list; - /* link caddr -> codec */ - struct hda_codec *caddr_tbl[HDA_MAX_CODEC_ADDRESS + 1]; - - struct mutex cmd_mutex; - struct mutex prepare_mutex; - - /* unsolicited event queue */ - struct hda_bus_unsolicited *unsol; - char workq_name[16]; - struct workqueue_struct *workq; /* common workqueue for codecs */ - - /* assigned PCMs */ - DECLARE_BITMAP(pcm_dev_bits, SNDRV_PCM_DEVICES); - - /* misc op flags */ - unsigned int needs_damn_long_delay :1; - unsigned int allow_bus_reset:1; /* allow bus reset at fatal error */ - unsigned int sync_write:1; /* sync after verb write */ - /* status for codec/controller */ - unsigned int shutdown :1; /* being unloaded */ - unsigned int rirb_error:1; /* error in codec communication */ - unsigned int response_reset:1; /* controller was reset */ - unsigned int in_reset:1; /* during reset operation */ - unsigned int power_keep_link_on:1; /* don't power off HDA link */ -}; - -/* - * codec preset - * - * Known codecs have the patch to build and set up the controls/PCMs - * better than the generic parser. - */ -struct hda_codec_preset { - unsigned int id; - unsigned int mask; - unsigned int subs; - unsigned int subs_mask; - unsigned int rev; - hda_nid_t afg, mfg; - const char *name; - 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); - int (*build_pcms)(struct hda_codec *codec); - int (*init)(struct hda_codec *codec); - void (*free)(struct hda_codec *codec); - void (*unsol_event)(struct hda_codec *codec, unsigned int res); - void (*set_power_state)(struct hda_codec *codec, hda_nid_t fg, - unsigned int power_state); -#ifdef CONFIG_PM - int (*suspend)(struct hda_codec *codec, pm_message_t state); - int (*post_suspend)(struct hda_codec *codec); - int (*pre_resume)(struct hda_codec *codec); - int (*resume)(struct hda_codec *codec); -#endif -#ifdef CONFIG_SND_HDA_POWER_SAVE - int (*check_power_status)(struct hda_codec *codec, hda_nid_t nid); -#endif - void (*reboot_notify)(struct hda_codec *codec); -}; - -/* record for amp information cache */ -struct hda_cache_head { - u32 key; /* hash key */ - u16 val; /* assigned value */ - u16 next; /* next link; -1 = terminal */ -}; - -struct hda_amp_info { - struct hda_cache_head head; - u32 amp_caps; /* amp capabilities */ - u16 vol[2]; /* current volume & mute */ -}; - -struct hda_cache_rec { - u16 hash[64]; /* hash table for index */ - struct snd_array buf; /* record entries */ -}; - -/* PCM callbacks */ -struct hda_pcm_ops { - int (*open)(struct hda_pcm_stream *info, struct hda_codec *codec, - struct snd_pcm_substream *substream); - int (*close)(struct hda_pcm_stream *info, struct hda_codec *codec, - struct snd_pcm_substream *substream); - int (*prepare)(struct hda_pcm_stream *info, struct hda_codec *codec, - unsigned int stream_tag, unsigned int format, - struct snd_pcm_substream *substream); - int (*cleanup)(struct hda_pcm_stream *info, struct hda_codec *codec, - struct snd_pcm_substream *substream); -}; - -/* PCM information for each substream */ -struct hda_pcm_stream { - unsigned int substreams; /* number of substreams, 0 = not exist*/ - unsigned int channels_min; /* min. number of channels */ - unsigned int channels_max; /* max. number of channels */ - hda_nid_t nid; /* default NID to query rates/formats/bps, or set up */ - u32 rates; /* supported rates */ - u64 formats; /* supported formats (SNDRV_PCM_FMTBIT_) */ - unsigned int maxbps; /* supported max. bit per sample */ - struct hda_pcm_ops ops; -}; - -/* PCM types */ -enum { - HDA_PCM_TYPE_AUDIO, - HDA_PCM_TYPE_SPDIF, - HDA_PCM_TYPE_HDMI, - HDA_PCM_TYPE_MODEM, - HDA_PCM_NTYPES -}; - -/* for PCM creation */ -struct hda_pcm { - char *name; - struct hda_pcm_stream stream[2]; - unsigned int pcm_type; /* HDA_PCM_TYPE_XXX */ - int device; /* device number to assign */ - struct snd_pcm *pcm; /* assigned PCM instance */ -}; - -/* codec information */ -struct hda_codec { - struct hda_bus *bus; - unsigned int addr; /* codec addr*/ - struct list_head list; /* list point */ - - hda_nid_t afg; /* AFG node id */ - hda_nid_t mfg; /* MFG node id */ - - /* ids */ - u8 afg_function_id; - u8 mfg_function_id; - u8 afg_unsol; - u8 mfg_unsol; - u32 vendor_id; - u32 subsystem_id; - u32 revision_id; - - /* detected preset */ - const struct hda_codec_preset *preset; - struct module *owner; - const char *vendor_name; /* codec vendor name */ - const char *chip_name; /* codec chip name */ - const char *modelname; /* model name for preset */ - - /* set by patch */ - struct hda_codec_ops patch_ops; - - /* PCM to create, set by patch_ops.build_pcms callback */ - unsigned int num_pcms; - struct hda_pcm *pcm_info; - - /* codec specific info */ - void *spec; - - /* beep device */ - struct hda_beep *beep; - unsigned int beep_mode; - - /* widget capabilities cache */ - unsigned int num_nodes; - hda_nid_t start_nid; - u32 *wcaps; - - struct snd_array mixers; /* list of assigned mixer elements */ - struct snd_array nids; /* list of mapped mixer elements */ - - struct hda_cache_rec amp_cache; /* cache for amp access */ - struct hda_cache_rec cmd_cache; /* cache for other commands */ - - struct snd_array conn_lists; /* connection-list array */ - - struct mutex spdif_mutex; - struct mutex control_mutex; - struct snd_array spdif_out; - unsigned int spdif_in_enable; /* SPDIF input enable? */ - const hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */ - struct snd_array init_pins; /* initial (BIOS) pin configurations */ - struct snd_array driver_pins; /* pin configs set by codec parser */ - struct snd_array cvt_setups; /* audio convert setups */ - -#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 */ - struct snd_array user_pins; /* default pin configs to override */ -#endif - - /* misc flags */ - unsigned int spdif_status_reset :1; /* needs to toggle SPDIF for each - * status change - * (e.g. Realtek codecs) - */ - unsigned int pin_amp_workaround:1; /* pin out-amp takes index - * (e.g. Conexant codecs) - */ - unsigned int single_adc_amp:1; /* adc in-amp takes no index - * (e.g. CX20549 codec) - */ - unsigned int no_sticky_stream:1; /* no sticky-PCM stream assignment */ - unsigned int pins_shutup:1; /* pins are shut up */ - unsigned int no_trigger_sense:1; /* don't trigger at pin-sensing */ - unsigned int ignore_misc_bit:1; /* ignore MISC_NO_PRESENCE bit */ - unsigned int no_jack_detect:1; /* Machine has no jack-detection */ -#ifdef CONFIG_SND_HDA_POWER_SAVE - unsigned int power_on :1; /* current (global) power-state */ - unsigned int power_transition :1; /* power-state in transition */ - int power_count; /* current (global) power refcount */ - struct delayed_work power_work; /* delayed task for powerdown */ - unsigned long power_on_acct; - unsigned long power_off_acct; - unsigned long power_jiffies; -#endif - - /* codec-specific additional proc output */ - void (*proc_widget_hook)(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid); - - /* jack detection */ - struct snd_array jacktbl; - -#ifdef CONFIG_SND_HDA_INPUT_JACK - /* jack detection */ - struct snd_array jacks; -#endif -}; - -/* direction */ -enum { - HDA_INPUT, HDA_OUTPUT -}; - - -/* - * constructors - */ -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 snd_hda_codec_configure(struct hda_codec *codec); - -/* - * low level functions - */ -unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, - int direct, - unsigned int verb, unsigned int parm); -int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct, - unsigned int verb, unsigned int parm); -#define snd_hda_param_read(codec, nid, param) \ - snd_hda_codec_read(codec, nid, 0, AC_VERB_PARAMETERS, param) -int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t *start_id); -int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t *conn_list, int max_conns); -int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t *conn_list, int max_conns); -int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid, - const hda_nid_t **listp); -int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int nums, - const hda_nid_t *list); -int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux, - hda_nid_t nid, int recursive); -int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, - u32 *ratesp, u64 *formatsp, unsigned int *bpsp); - -struct hda_verb { - hda_nid_t nid; - u32 verb; - u32 param; -}; - -void snd_hda_sequence_write(struct hda_codec *codec, - const struct hda_verb *seq); - -/* unsolicited event */ -int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex); - -/* cached write */ -#ifdef CONFIG_PM -int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, - int direct, unsigned int verb, unsigned int parm); -void snd_hda_sequence_write_cache(struct hda_codec *codec, - const struct hda_verb *seq); -int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid, - int direct, unsigned int verb, unsigned int parm); -void snd_hda_codec_resume_cache(struct hda_codec *codec); -#else -#define snd_hda_codec_write_cache snd_hda_codec_write -#define snd_hda_codec_update_cache snd_hda_codec_write -#define snd_hda_sequence_write_cache snd_hda_sequence_write -#endif - -/* the struct for codec->pin_configs */ -struct hda_pincfg { - hda_nid_t nid; - unsigned char ctrl; /* current pin control value */ - unsigned char pad; /* reserved */ - unsigned int cfg; /* default configuration */ -}; - -unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid); -int snd_hda_codec_set_pincfg(struct hda_codec *codec, hda_nid_t nid, - unsigned int cfg); -int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list, - hda_nid_t nid, unsigned int cfg); /* for hwdep */ -void snd_hda_shutup_pins(struct hda_codec *codec); - -/* SPDIF controls */ -struct hda_spdif_out { - hda_nid_t nid; /* Converter nid values relate to */ - unsigned int status; /* IEC958 status bits */ - unsigned short ctls; /* SPDIF control bits */ -}; -struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec, - hda_nid_t nid); -void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx); -void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid); - -/* - * 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); - -int snd_hda_codec_prepare(struct hda_codec *codec, - struct hda_pcm_stream *hinfo, - unsigned int stream, - unsigned int format, - struct snd_pcm_substream *substream); -void snd_hda_codec_cleanup(struct hda_codec *codec, - struct hda_pcm_stream *hinfo, - struct snd_pcm_substream *substream); - -void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, - u32 stream_tag, - int channel_id, int format); -void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid, - int do_now); -#define snd_hda_codec_cleanup_stream(codec, nid) \ - __snd_hda_codec_cleanup_stream(codec, nid, 0) -unsigned int snd_hda_calc_stream_format(unsigned int rate, - unsigned int channels, - unsigned int format, - unsigned int maxbps, - unsigned short spdif_ctls); -int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid, - unsigned int format); - -/* - * Misc - */ -void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen); -void snd_hda_bus_reboot_notify(struct hda_bus *bus); -void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, - unsigned int power_state, - bool eapd_workaround); - -/* - * power management - */ -#ifdef CONFIG_PM -int snd_hda_suspend(struct hda_bus *bus); -int snd_hda_resume(struct hda_bus *bus); -#endif - -static inline -int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid) -{ -#ifdef CONFIG_SND_HDA_POWER_SAVE - if (codec->patch_ops.check_power_status) - return codec->patch_ops.check_power_status(codec, nid); -#endif - return 0; -} - -/* - * 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 -void snd_hda_update_power_acct(struct hda_codec *codec); -#else -static inline void snd_hda_power_up(struct hda_codec *codec) {} -static inline void snd_hda_power_down(struct hda_codec *codec) {} -#define snd_hda_codec_needs_resume(codec) 1 -#endif - -#ifdef CONFIG_SND_HDA_PATCH_LOADER -/* - * patch firmware - */ -int snd_hda_load_patch(struct hda_bus *bus, const char *patch); -#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/ANDROID_3.4.5/sound/pci/hda/hda_eld.c b/ANDROID_3.4.5/sound/pci/hda/hda_eld.c deleted file mode 100644 index 4c054f44..00000000 --- a/ANDROID_3.4.5/sound/pci/hda/hda_eld.c +++ /dev/null @@ -1,671 +0,0 @@ -/* - * 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 <linux/slab.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 int hdmi_get_eld_data(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 - return val; -} - -#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->sample_bits = 0; - a->max_bitrate = 0; - - 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); - 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 + 1); - - 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); - } - - /* - * HDMI sink's ELD info cannot always be retrieved for now, e.g. - * in console or for audio devices. Assume the highest speakers - * configuration, to _not_ prohibit multi-channel audio playback. - */ - if (!e->spk_alloc) - e->spk_alloc = 0xffff; - - e->eld_valid = true; - return 0; - -out_fail: - return -EINVAL; -} - -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; - - /* - * ELD size is initialized to zero in caller function. If no errors and - * ELD is valid, actual eld_size is assigned in hdmi_update_eld() - */ - - 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 > ELD_MAX_SIZE) { - snd_printd(KERN_INFO "HDMI: invalid ELD buf size %d\n", size); - return -ERANGE; - } - - /* set ELD buffer */ - buf = eld->eld_buffer; - - for (i = 0; i < size; i++) { - unsigned int val = hdmi_get_eld_data(codec, nid, i); - /* - * Graphics driver might be writing to ELD buffer right now. - * Just abort. The caller will repoll after a while. - */ - if (!(val & AC_ELDD_ELD_VALID)) { - snd_printd(KERN_INFO - "HDMI: invalid ELD data byte %d\n", i); - ret = -EINVAL; - goto error; - } - val &= AC_ELDD_ELD_DATA; - /* - * The first byte cannot be zero. This can happen on some DVI - * connections. Some Intel chips may also need some 250ms delay - * to return non-zero ELD data, even when the graphics driver - * correctly writes ELD content before setting ELD_valid bit. - */ - if (!val && !i) { - snd_printdd(KERN_INFO "HDMI: 0 ELD data\n"); - ret = -EINVAL; - goto error; - } - buf[i] = val; - } - - ret = hdmi_update_eld(eld, buf, size); - -error: - return ret; -} - -/** - * SNDRV_PCM_RATE_* and AC_PAR_PCM values don't match, print correct rates with - * hdmi-specific routine. - */ -static void hdmi_print_pcm_rates(int pcm, char *buf, int buflen) -{ - static unsigned int alsa_rates[] = { - 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, - 88200, 96000, 176400, 192000, 384000 - }; - int i, j; - - for (i = 0, j = 0; i < ARRAY_SIZE(alsa_rates); i++) - if (pcm & (1 << i)) - j += snprintf(buf + j, buflen - j, " %d", - alsa_rates[i]); - - buf[j] = '\0'; /* necessary when j == 0 */ -} - -#define SND_PRINT_RATES_ADVISED_BUFSIZE 80 - -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; - - hdmi_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'; - - _snd_printd(SND_PR_VERBOSE, "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; - - _snd_printd(SND_PR_VERBOSE, "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)); - _snd_printd(SND_PR_VERBOSE, "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); - - hdmi_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_present\t\t%d\n", e->monitor_present); - snd_iprintf(buffer, "eld_valid\t\t%d\n", e->eld_valid); - if (!e->eld_valid) - return; - 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; - unsigned 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, "monitor_present")) - e->monitor_present = val; - else if (!strcmp(name, "eld_valid")) - e->eld_valid = val; - else if (!strcmp(name, "connection_type")) - e->conn_type = val; - else if (!strcmp(name, "port_id")) - e->port_id = val; - 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 >= ELD_MAX_SAD) - 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, - int index) -{ - char name[32]; - struct snd_info_entry *entry; - int err; - - snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index); - err = snd_card_proc_new(codec->bus->card, name, &entry); - if (err < 0) - return err; - - 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 */ - -/* update PCM info based on ELD */ -void snd_hdmi_eld_update_pcm_info(struct hdmi_eld *eld, - struct hda_pcm_stream *hinfo) -{ - u32 rates; - u64 formats; - unsigned int maxbps; - unsigned int channels_max; - int i; - - /* assume basic audio support (the basic audio flag is not in ELD; - * however, all audio capable sinks are required to support basic - * audio) */ - rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000; - formats = SNDRV_PCM_FMTBIT_S16_LE; - maxbps = 16; - channels_max = 2; - for (i = 0; i < eld->sad_count; i++) { - struct cea_sad *a = &eld->sad[i]; - rates |= a->rates; - if (a->channels > channels_max) - channels_max = a->channels; - if (a->format == AUDIO_CODING_TYPE_LPCM) { - if (a->sample_bits & AC_SUPPCM_BITS_20) { - formats |= SNDRV_PCM_FMTBIT_S32_LE; - if (maxbps < 20) - maxbps = 20; - } - if (a->sample_bits & AC_SUPPCM_BITS_24) { - formats |= SNDRV_PCM_FMTBIT_S32_LE; - if (maxbps < 24) - maxbps = 24; - } - } - } - - /* restrict the parameters by the values the codec provides */ - hinfo->rates &= rates; - hinfo->formats &= formats; - hinfo->maxbps = min(hinfo->maxbps, maxbps); - hinfo->channels_max = min(hinfo->channels_max, channels_max); -} diff --git a/ANDROID_3.4.5/sound/pci/hda/hda_generic.c b/ANDROID_3.4.5/sound/pci/hda/hda_generic.c deleted file mode 100644 index 431bf868..00000000 --- a/ANDROID_3.4.5/sound/pci/hda/hda_generic.c +++ /dev/null @@ -1,1085 +0,0 @@ -/* - * Universal Interface for Intel High Definition Audio Codec - * - * Generic widget tree parser - * - * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> - * - * 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 <linux/slab.h> -#include <linux/export.h> -#include <sound/core.h> -#include "hda_codec.h" -#include "hda_local.h" - -/* widget node for parsing */ -struct hda_gnode { - hda_nid_t nid; /* NID of this widget */ - unsigned short nconns; /* number of input connections */ - hda_nid_t *conn_list; - hda_nid_t slist[2]; /* temporay list */ - unsigned int wid_caps; /* widget capabilities */ - unsigned char type; /* widget type */ - unsigned char pin_ctl; /* pin controls */ - unsigned char checked; /* the flag indicates that the node is already parsed */ - unsigned int pin_caps; /* pin widget capabilities */ - unsigned int def_cfg; /* default configuration */ - unsigned int amp_out_caps; /* AMP out capabilities */ - unsigned int amp_in_caps; /* AMP in capabilities */ - struct list_head list; -}; - -/* patch-specific record */ - -#define MAX_PCM_VOLS 2 -struct pcm_vol { - struct hda_gnode *node; /* Node for PCM volume */ - unsigned int index; /* connection of PCM volume */ -}; - -struct hda_gspec { - struct hda_gnode *dac_node[2]; /* DAC node */ - struct hda_gnode *out_pin_node[2]; /* Output pin (Line-Out) node */ - struct pcm_vol pcm_vol[MAX_PCM_VOLS]; /* PCM volumes */ - unsigned int pcm_vol_nodes; /* number of PCM volumes */ - - struct hda_gnode *adc_node; /* ADC node */ - struct hda_gnode *cap_vol_node; /* Node for capture volume */ - unsigned int cur_cap_src; /* current capture source */ - struct hda_input_mux input_mux; - - unsigned int def_amp_in_caps; - unsigned int def_amp_out_caps; - - struct hda_pcm pcm_rec; /* PCM information */ - - struct list_head nid_list; /* list of widgets */ - -#ifdef CONFIG_SND_HDA_POWER_SAVE -#define MAX_LOOPBACK_AMPS 7 - struct hda_loopback_check loopback; - int num_loopbacks; - struct hda_amp_list loopback_list[MAX_LOOPBACK_AMPS + 1]; -#endif -}; - -/* - * retrieve the default device type from the default config value - */ -#define defcfg_type(node) (((node)->def_cfg & AC_DEFCFG_DEVICE) >> \ - AC_DEFCFG_DEVICE_SHIFT) -#define defcfg_location(node) (((node)->def_cfg & AC_DEFCFG_LOCATION) >> \ - AC_DEFCFG_LOCATION_SHIFT) -#define defcfg_port_conn(node) (((node)->def_cfg & AC_DEFCFG_PORT_CONN) >> \ - AC_DEFCFG_PORT_CONN_SHIFT) - -/* - * destructor - */ -static void snd_hda_generic_free(struct hda_codec *codec) -{ - struct hda_gspec *spec = codec->spec; - struct hda_gnode *node, *n; - - if (! spec) - return; - /* free all widgets */ - list_for_each_entry_safe(node, n, &spec->nid_list, list) { - if (node->conn_list != node->slist) - kfree(node->conn_list); - kfree(node); - } - kfree(spec); -} - - -/* - * add a new widget node and read its attributes - */ -static int add_new_node(struct hda_codec *codec, struct hda_gspec *spec, hda_nid_t nid) -{ - struct hda_gnode *node; - int nconns; - hda_nid_t conn_list[HDA_MAX_CONNECTIONS]; - - node = kzalloc(sizeof(*node), GFP_KERNEL); - if (node == NULL) - return -ENOMEM; - node->nid = nid; - node->wid_caps = get_wcaps(codec, nid); - node->type = get_wcaps_type(node->wid_caps); - if (node->wid_caps & AC_WCAP_CONN_LIST) { - nconns = snd_hda_get_connections(codec, nid, conn_list, - HDA_MAX_CONNECTIONS); - if (nconns < 0) { - kfree(node); - return nconns; - } - } else { - nconns = 0; - } - if (nconns <= ARRAY_SIZE(node->slist)) - node->conn_list = node->slist; - else { - node->conn_list = kmalloc(sizeof(hda_nid_t) * nconns, - GFP_KERNEL); - if (! node->conn_list) { - snd_printk(KERN_ERR "hda-generic: cannot malloc\n"); - kfree(node); - return -ENOMEM; - } - } - memcpy(node->conn_list, conn_list, nconns * sizeof(hda_nid_t)); - node->nconns = nconns; - - if (node->type == AC_WID_PIN) { - node->pin_caps = snd_hda_query_pin_caps(codec, node->nid); - node->pin_ctl = snd_hda_codec_read(codec, node->nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - node->def_cfg = snd_hda_codec_get_pincfg(codec, node->nid); - } - - if (node->wid_caps & AC_WCAP_OUT_AMP) { - if (node->wid_caps & AC_WCAP_AMP_OVRD) - node->amp_out_caps = snd_hda_param_read(codec, node->nid, AC_PAR_AMP_OUT_CAP); - if (! node->amp_out_caps) - node->amp_out_caps = spec->def_amp_out_caps; - } - if (node->wid_caps & AC_WCAP_IN_AMP) { - if (node->wid_caps & AC_WCAP_AMP_OVRD) - node->amp_in_caps = snd_hda_param_read(codec, node->nid, AC_PAR_AMP_IN_CAP); - if (! node->amp_in_caps) - node->amp_in_caps = spec->def_amp_in_caps; - } - list_add_tail(&node->list, &spec->nid_list); - return 0; -} - -/* - * build the AFG subtree - */ -static int build_afg_tree(struct hda_codec *codec) -{ - struct hda_gspec *spec = codec->spec; - int i, nodes, err; - hda_nid_t nid; - - if (snd_BUG_ON(!spec)) - return -EINVAL; - - spec->def_amp_out_caps = snd_hda_param_read(codec, codec->afg, AC_PAR_AMP_OUT_CAP); - spec->def_amp_in_caps = snd_hda_param_read(codec, codec->afg, AC_PAR_AMP_IN_CAP); - - nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid); - if (! nid || nodes < 0) { - printk(KERN_ERR "Invalid AFG subtree\n"); - return -EINVAL; - } - - /* parse all nodes belonging to the AFG */ - for (i = 0; i < nodes; i++, nid++) { - if ((err = add_new_node(codec, spec, nid)) < 0) - return err; - } - - return 0; -} - - -/* - * look for the node record for the given NID - */ -/* FIXME: should avoid the braindead linear search */ -static struct hda_gnode *hda_get_node(struct hda_gspec *spec, hda_nid_t nid) -{ - struct hda_gnode *node; - - list_for_each_entry(node, &spec->nid_list, list) { - if (node->nid == nid) - return node; - } - return NULL; -} - -/* - * unmute (and set max vol) the output amplifier - */ -static int unmute_output(struct hda_codec *codec, struct hda_gnode *node) -{ - unsigned int val, ofs; - snd_printdd("UNMUTE OUT: NID=0x%x\n", node->nid); - val = (node->amp_out_caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; - ofs = (node->amp_out_caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; - if (val >= ofs) - val -= ofs; - snd_hda_codec_amp_stereo(codec, node->nid, HDA_OUTPUT, 0, 0xff, val); - return 0; -} - -/* - * unmute (and set max vol) the input amplifier - */ -static int unmute_input(struct hda_codec *codec, struct hda_gnode *node, unsigned int index) -{ - unsigned int val, ofs; - snd_printdd("UNMUTE IN: NID=0x%x IDX=0x%x\n", node->nid, index); - val = (node->amp_in_caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; - ofs = (node->amp_in_caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; - if (val >= ofs) - val -= ofs; - snd_hda_codec_amp_stereo(codec, node->nid, HDA_INPUT, index, 0xff, val); - return 0; -} - -/* - * select the input connection of the given node. - */ -static int select_input_connection(struct hda_codec *codec, struct hda_gnode *node, - unsigned int index) -{ - snd_printdd("CONNECT: NID=0x%x IDX=0x%x\n", node->nid, index); - return snd_hda_codec_write_cache(codec, node->nid, 0, - AC_VERB_SET_CONNECT_SEL, index); -} - -/* - * clear checked flag of each node in the node list - */ -static void clear_check_flags(struct hda_gspec *spec) -{ - struct hda_gnode *node; - - list_for_each_entry(node, &spec->nid_list, list) { - node->checked = 0; - } -} - -/* - * parse the output path recursively until reach to an audio output widget - * - * returns 0 if not found, 1 if found, or a negative error code. - */ -static int parse_output_path(struct hda_codec *codec, struct hda_gspec *spec, - struct hda_gnode *node, int dac_idx) -{ - int i, err; - struct hda_gnode *child; - - if (node->checked) - return 0; - - node->checked = 1; - if (node->type == AC_WID_AUD_OUT) { - if (node->wid_caps & AC_WCAP_DIGITAL) { - snd_printdd("Skip Digital OUT node %x\n", node->nid); - return 0; - } - snd_printdd("AUD_OUT found %x\n", node->nid); - if (spec->dac_node[dac_idx]) { - /* already DAC node is assigned, just unmute & connect */ - return node == spec->dac_node[dac_idx]; - } - spec->dac_node[dac_idx] = node; - if ((node->wid_caps & AC_WCAP_OUT_AMP) && - spec->pcm_vol_nodes < MAX_PCM_VOLS) { - spec->pcm_vol[spec->pcm_vol_nodes].node = node; - spec->pcm_vol[spec->pcm_vol_nodes].index = 0; - spec->pcm_vol_nodes++; - } - return 1; /* found */ - } - - for (i = 0; i < node->nconns; i++) { - child = hda_get_node(spec, node->conn_list[i]); - if (! child) - continue; - err = parse_output_path(codec, spec, child, dac_idx); - if (err < 0) - return err; - else if (err > 0) { - /* found one, - * select the path, unmute both input and output - */ - if (node->nconns > 1) - select_input_connection(codec, node, i); - unmute_input(codec, node, i); - unmute_output(codec, node); - if (spec->dac_node[dac_idx] && - spec->pcm_vol_nodes < MAX_PCM_VOLS && - !(spec->dac_node[dac_idx]->wid_caps & - AC_WCAP_OUT_AMP)) { - if ((node->wid_caps & AC_WCAP_IN_AMP) || - (node->wid_caps & AC_WCAP_OUT_AMP)) { - int n = spec->pcm_vol_nodes; - spec->pcm_vol[n].node = node; - spec->pcm_vol[n].index = i; - spec->pcm_vol_nodes++; - } - } - return 1; - } - } - return 0; -} - -/* - * Look for the output PIN widget with the given jack type - * and parse the output path to that PIN. - * - * Returns the PIN node when the path to DAC is established. - */ -static struct hda_gnode *parse_output_jack(struct hda_codec *codec, - struct hda_gspec *spec, - int jack_type) -{ - struct hda_gnode *node; - int err; - - list_for_each_entry(node, &spec->nid_list, list) { - if (node->type != AC_WID_PIN) - continue; - /* output capable? */ - if (! (node->pin_caps & AC_PINCAP_OUT)) - continue; - if (defcfg_port_conn(node) == AC_JACK_PORT_NONE) - continue; /* unconnected */ - if (jack_type >= 0) { - if (jack_type != defcfg_type(node)) - continue; - if (node->wid_caps & AC_WCAP_DIGITAL) - continue; /* skip SPDIF */ - } else { - /* output as default? */ - if (! (node->pin_ctl & AC_PINCTL_OUT_EN)) - continue; - } - clear_check_flags(spec); - err = parse_output_path(codec, spec, node, 0); - if (err < 0) - return NULL; - if (! err && spec->out_pin_node[0]) { - err = parse_output_path(codec, spec, node, 1); - if (err < 0) - return NULL; - } - if (err > 0) { - /* unmute the PIN output */ - unmute_output(codec, node); - /* set PIN-Out enable */ - snd_hda_codec_write_cache(codec, node->nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - AC_PINCTL_OUT_EN | - ((node->pin_caps & AC_PINCAP_HP_DRV) ? - AC_PINCTL_HP_EN : 0)); - return node; - } - } - return NULL; -} - - -/* - * parse outputs - */ -static int parse_output(struct hda_codec *codec) -{ - struct hda_gspec *spec = codec->spec; - struct hda_gnode *node; - - /* - * Look for the output PIN widget - */ - /* first, look for the line-out pin */ - node = parse_output_jack(codec, spec, AC_JACK_LINE_OUT); - if (node) /* found, remember the PIN node */ - spec->out_pin_node[0] = node; - else { - /* if no line-out is found, try speaker out */ - node = parse_output_jack(codec, spec, AC_JACK_SPEAKER); - if (node) - spec->out_pin_node[0] = node; - } - /* look for the HP-out pin */ - node = parse_output_jack(codec, spec, AC_JACK_HP_OUT); - if (node) { - if (! spec->out_pin_node[0]) - spec->out_pin_node[0] = node; - else - spec->out_pin_node[1] = node; - } - - if (! spec->out_pin_node[0]) { - /* no line-out or HP pins found, - * then choose for the first output pin - */ - spec->out_pin_node[0] = parse_output_jack(codec, spec, -1); - if (! spec->out_pin_node[0]) - snd_printd("hda_generic: no proper output path found\n"); - } - - return 0; -} - -/* - * input MUX - */ - -/* control callbacks */ -static int capture_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gspec *spec = codec->spec; - return snd_hda_input_mux_info(&spec->input_mux, uinfo); -} - -static int capture_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gspec *spec = codec->spec; - - ucontrol->value.enumerated.item[0] = spec->cur_cap_src; - return 0; -} - -static int capture_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gspec *spec = codec->spec; - return snd_hda_input_mux_put(codec, &spec->input_mux, ucontrol, - spec->adc_node->nid, &spec->cur_cap_src); -} - -/* - * return the string name of the given input PIN widget - */ -static const char *get_input_type(struct hda_gnode *node, unsigned int *pinctl) -{ - unsigned int location = defcfg_location(node); - switch (defcfg_type(node)) { - case AC_JACK_LINE_IN: - if ((location & 0x0f) == AC_JACK_LOC_FRONT) - return "Front Line"; - return "Line"; - case AC_JACK_CD: -#if 0 - if (pinctl) - *pinctl |= AC_PINCTL_VREF_GRD; -#endif - return "CD"; - case AC_JACK_AUX: - if ((location & 0x0f) == AC_JACK_LOC_FRONT) - return "Front Aux"; - return "Aux"; - case AC_JACK_MIC_IN: - if (pinctl && - (node->pin_caps & - (AC_PINCAP_VREF_80 << AC_PINCAP_VREF_SHIFT))) - *pinctl |= AC_PINCTL_VREF_80; - if ((location & 0x0f) == AC_JACK_LOC_FRONT) - return "Front Mic"; - return "Mic"; - case AC_JACK_SPDIF_IN: - return "SPDIF"; - case AC_JACK_DIG_OTHER_IN: - return "Digital"; - } - return NULL; -} - -/* - * parse the nodes recursively until reach to the input PIN - * - * returns 0 if not found, 1 if found, or a negative error code. - */ -static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec, - struct hda_gnode *node, int idx) -{ - int i, err; - unsigned int pinctl; - const char *type; - - if (node->checked) - return 0; - - node->checked = 1; - if (node->type != AC_WID_PIN) { - for (i = 0; i < node->nconns; i++) { - struct hda_gnode *child; - child = hda_get_node(spec, node->conn_list[i]); - if (! child) - continue; - err = parse_adc_sub_nodes(codec, spec, child, idx); - if (err < 0) - return err; - if (err > 0) { - /* found one, - * select the path, unmute both input and output - */ - if (node->nconns > 1) - select_input_connection(codec, node, i); - unmute_input(codec, node, i); - unmute_output(codec, node); - return err; - } - } - return 0; - } - - /* input capable? */ - if (! (node->pin_caps & AC_PINCAP_IN)) - return 0; - - if (defcfg_port_conn(node) == AC_JACK_PORT_NONE) - return 0; /* unconnected */ - - if (node->wid_caps & AC_WCAP_DIGITAL) - return 0; /* skip SPDIF */ - - if (spec->input_mux.num_items >= HDA_MAX_NUM_INPUTS) { - snd_printk(KERN_ERR "hda_generic: Too many items for capture\n"); - return -EINVAL; - } - - pinctl = AC_PINCTL_IN_EN; - /* create a proper capture source label */ - type = get_input_type(node, &pinctl); - if (! type) { - /* input as default? */ - if (! (node->pin_ctl & AC_PINCTL_IN_EN)) - return 0; - type = "Input"; - } - snd_hda_add_imux_item(&spec->input_mux, type, idx, NULL); - - /* unmute the PIN external input */ - unmute_input(codec, node, 0); /* index = 0? */ - /* set PIN-In enable */ - snd_hda_codec_write_cache(codec, node->nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl); - - return 1; /* found */ -} - -/* - * parse input - */ -static int parse_input_path(struct hda_codec *codec, struct hda_gnode *adc_node) -{ - struct hda_gspec *spec = codec->spec; - struct hda_gnode *node; - int i, err; - - snd_printdd("AUD_IN = %x\n", adc_node->nid); - clear_check_flags(spec); - - // awk added - fixed no recording due to muted widget - unmute_input(codec, adc_node, 0); - - /* - * check each connection of the ADC - * if it reaches to a proper input PIN, add the path as the - * input path. - */ - /* first, check the direct connections to PIN widgets */ - for (i = 0; i < adc_node->nconns; i++) { - node = hda_get_node(spec, adc_node->conn_list[i]); - if (node && node->type == AC_WID_PIN) { - err = parse_adc_sub_nodes(codec, spec, node, i); - if (err < 0) - return err; - } - } - /* ... then check the rests, more complicated connections */ - for (i = 0; i < adc_node->nconns; i++) { - node = hda_get_node(spec, adc_node->conn_list[i]); - if (node && node->type != AC_WID_PIN) { - err = parse_adc_sub_nodes(codec, spec, node, i); - if (err < 0) - return err; - } - } - - if (! spec->input_mux.num_items) - return 0; /* no input path found... */ - - snd_printdd("[Capture Source] NID=0x%x, #SRC=%d\n", adc_node->nid, spec->input_mux.num_items); - for (i = 0; i < spec->input_mux.num_items; i++) - snd_printdd(" [%s] IDX=0x%x\n", spec->input_mux.items[i].label, - spec->input_mux.items[i].index); - - spec->adc_node = adc_node; - return 1; -} - -/* - * parse input - */ -static int parse_input(struct hda_codec *codec) -{ - struct hda_gspec *spec = codec->spec; - struct hda_gnode *node; - int err; - - /* - * At first we look for an audio input widget. - * If it reaches to certain input PINs, we take it as the - * input path. - */ - list_for_each_entry(node, &spec->nid_list, list) { - if (node->wid_caps & AC_WCAP_DIGITAL) - continue; /* skip SPDIF */ - if (node->type == AC_WID_AUD_IN) { - err = parse_input_path(codec, node); - if (err < 0) - return err; - else if (err > 0) - return 0; - } - } - snd_printd("hda_generic: no proper input path found\n"); - return 0; -} - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static void add_input_loopback(struct hda_codec *codec, hda_nid_t nid, - int dir, int idx) -{ - struct hda_gspec *spec = codec->spec; - struct hda_amp_list *p; - - if (spec->num_loopbacks >= MAX_LOOPBACK_AMPS) { - snd_printk(KERN_ERR "hda_generic: Too many loopback ctls\n"); - return; - } - p = &spec->loopback_list[spec->num_loopbacks++]; - p->nid = nid; - p->dir = dir; - p->idx = idx; - spec->loopback.amplist = spec->loopback_list; -} -#else -#define add_input_loopback(codec,nid,dir,idx) -#endif - -/* - * create mixer controls if possible - */ -static int create_mixer(struct hda_codec *codec, struct hda_gnode *node, - unsigned int index, const char *type, - const char *dir_sfx, int is_loopback) -{ - char name[32]; - int err; - int created = 0; - struct snd_kcontrol_new knew; - - if (type) - sprintf(name, "%s %s Switch", type, dir_sfx); - else - sprintf(name, "%s Switch", dir_sfx); - if ((node->wid_caps & AC_WCAP_IN_AMP) && - (node->amp_in_caps & AC_AMPCAP_MUTE)) { - knew = (struct snd_kcontrol_new)HDA_CODEC_MUTE(name, node->nid, index, HDA_INPUT); - if (is_loopback) - add_input_loopback(codec, node->nid, HDA_INPUT, index); - snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index); - err = snd_hda_ctl_add(codec, node->nid, - 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_MUTE)) { - knew = (struct snd_kcontrol_new)HDA_CODEC_MUTE(name, node->nid, 0, HDA_OUTPUT); - if (is_loopback) - add_input_loopback(codec, node->nid, HDA_OUTPUT, 0); - snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid); - err = snd_hda_ctl_add(codec, node->nid, - snd_ctl_new1(&knew, codec)); - if (err < 0) - return err; - created = 1; - } - - if (type) - sprintf(name, "%s %s Volume", type, dir_sfx); - else - sprintf(name, "%s Volume", dir_sfx); - if ((node->wid_caps & AC_WCAP_IN_AMP) && - (node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) { - knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, index, HDA_INPUT); - snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index); - err = snd_hda_ctl_add(codec, node->nid, - 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); - err = snd_hda_ctl_add(codec, node->nid, - snd_ctl_new1(&knew, codec)); - if (err < 0) - return err; - created = 1; - } - - return created; -} - -/* - * check whether the controls with the given name and direction suffix already exist - */ -static int check_existing_control(struct hda_codec *codec, const char *type, const char *dir) -{ - struct snd_ctl_elem_id id; - memset(&id, 0, sizeof(id)); - sprintf(id.name, "%s %s Volume", type, dir); - id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - if (snd_ctl_find_id(codec->bus->card, &id)) - return 1; - sprintf(id.name, "%s %s Switch", type, dir); - id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - if (snd_ctl_find_id(codec->bus->card, &id)) - return 1; - return 0; -} - -/* - * build output mixer controls - */ -static int create_output_mixers(struct hda_codec *codec, - const char * const *names) -{ - struct hda_gspec *spec = codec->spec; - int i, err; - - for (i = 0; i < spec->pcm_vol_nodes; i++) { - err = create_mixer(codec, spec->pcm_vol[i].node, - spec->pcm_vol[i].index, - names[i], "Playback", 0); - if (err < 0) - return err; - } - return 0; -} - -static int build_output_controls(struct hda_codec *codec) -{ - struct hda_gspec *spec = codec->spec; - static const char * const types_speaker[] = { "Speaker", "Headphone" }; - static const char * const types_line[] = { "Front", "Headphone" }; - - switch (spec->pcm_vol_nodes) { - case 1: - return create_mixer(codec, spec->pcm_vol[0].node, - spec->pcm_vol[0].index, - "Master", "Playback", 0); - case 2: - if (defcfg_type(spec->out_pin_node[0]) == AC_JACK_SPEAKER) - return create_output_mixers(codec, types_speaker); - else - return create_output_mixers(codec, types_line); - } - return 0; -} - -/* create capture volume/switch */ -static int build_input_controls(struct hda_codec *codec) -{ - struct hda_gspec *spec = codec->spec; - struct hda_gnode *adc_node = spec->adc_node; - int i, err; - static struct snd_kcontrol_new cap_sel = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = capture_source_info, - .get = capture_source_get, - .put = capture_source_put, - }; - - if (! adc_node || ! spec->input_mux.num_items) - return 0; /* not found */ - - spec->cur_cap_src = 0; - select_input_connection(codec, adc_node, - spec->input_mux.items[0].index); - - /* create capture volume and switch controls if the ADC has an amp */ - /* do we have only a single item? */ - if (spec->input_mux.num_items == 1) { - err = create_mixer(codec, adc_node, - spec->input_mux.items[0].index, - NULL, "Capture", 0); - if (err < 0) - return err; - return 0; - } - - /* create input MUX if multiple sources are available */ - err = snd_hda_ctl_add(codec, spec->adc_node->nid, - snd_ctl_new1(&cap_sel, codec)); - if (err < 0) - return err; - - /* no volume control? */ - if (! (adc_node->wid_caps & AC_WCAP_IN_AMP) || - ! (adc_node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) - return 0; - - for (i = 0; i < spec->input_mux.num_items; i++) { - struct snd_kcontrol_new knew; - char name[32]; - sprintf(name, "%s Capture Volume", - spec->input_mux.items[i].label); - knew = (struct snd_kcontrol_new) - HDA_CODEC_VOLUME(name, adc_node->nid, - spec->input_mux.items[i].index, - HDA_INPUT); - err = snd_hda_ctl_add(codec, adc_node->nid, - snd_ctl_new1(&knew, codec)); - if (err < 0) - return err; - } - - return 0; -} - - -/* - * parse the nodes recursively until reach to the output PIN. - * - * returns 0 - if not found, - * 1 - if found, but no mixer is created - * 2 - if found and mixer was already created, (just skip) - * a negative error code - */ -static int parse_loopback_path(struct hda_codec *codec, struct hda_gspec *spec, - struct hda_gnode *node, struct hda_gnode *dest_node, - const char *type) -{ - int i, err; - - if (node->checked) - return 0; - - node->checked = 1; - if (node == dest_node) { - /* loopback connection found */ - return 1; - } - - for (i = 0; i < node->nconns; i++) { - struct hda_gnode *child = hda_get_node(spec, node->conn_list[i]); - if (! child) - continue; - err = parse_loopback_path(codec, spec, child, dest_node, type); - if (err < 0) - return err; - else if (err >= 1) { - if (err == 1) { - err = create_mixer(codec, node, i, type, - "Playback", 1); - if (err < 0) - return err; - if (err > 0) - return 2; /* ok, created */ - /* not created, maybe in the lower path */ - err = 1; - } - /* connect and unmute */ - if (node->nconns > 1) - select_input_connection(codec, node, i); - unmute_input(codec, node, i); - unmute_output(codec, node); - return err; - } - } - return 0; -} - -/* - * parse the tree and build the loopback controls - */ -static int build_loopback_controls(struct hda_codec *codec) -{ - struct hda_gspec *spec = codec->spec; - struct hda_gnode *node; - int err; - const char *type; - - if (! spec->out_pin_node[0]) - return 0; - - list_for_each_entry(node, &spec->nid_list, list) { - if (node->type != AC_WID_PIN) - continue; - /* input capable? */ - if (! (node->pin_caps & AC_PINCAP_IN)) - return 0; - type = get_input_type(node, NULL); - if (type) { - if (check_existing_control(codec, type, "Playback")) - continue; - clear_check_flags(spec); - err = parse_loopback_path(codec, spec, - spec->out_pin_node[0], - node, type); - if (err < 0) - return err; - if (! err) - continue; - } - } - return 0; -} - -/* - * build mixer controls - */ -static int build_generic_controls(struct hda_codec *codec) -{ - int err; - - if ((err = build_input_controls(codec)) < 0 || - (err = build_output_controls(codec)) < 0 || - (err = build_loopback_controls(codec)) < 0) - return err; - - return 0; -} - -/* - * PCM - */ -static struct hda_pcm_stream generic_pcm_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, -}; - -static int generic_pcm2_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct hda_gspec *spec = codec->spec; - - snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); - snd_hda_codec_setup_stream(codec, spec->dac_node[1]->nid, - stream_tag, 0, format); - return 0; -} - -static int generic_pcm2_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hda_gspec *spec = codec->spec; - - snd_hda_codec_cleanup_stream(codec, hinfo->nid); - snd_hda_codec_cleanup_stream(codec, spec->dac_node[1]->nid); - return 0; -} - -static int build_generic_pcms(struct hda_codec *codec) -{ - struct hda_gspec *spec = codec->spec; - struct hda_pcm *info = &spec->pcm_rec; - - if (! spec->dac_node[0] && ! spec->adc_node) { - snd_printd("hda_generic: no PCM found\n"); - return 0; - } - - codec->num_pcms = 1; - codec->pcm_info = info; - - info->name = "HDA Generic"; - if (spec->dac_node[0]) { - info->stream[0] = generic_pcm_playback; - info->stream[0].nid = spec->dac_node[0]->nid; - if (spec->dac_node[1]) { - info->stream[0].ops.prepare = generic_pcm2_prepare; - info->stream[0].ops.cleanup = generic_pcm2_cleanup; - } - } - if (spec->adc_node) { - info->stream[1] = generic_pcm_playback; - info->stream[1].nid = spec->adc_node->nid; - } - - return 0; -} - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static int generic_check_power_status(struct hda_codec *codec, hda_nid_t nid) -{ - struct hda_gspec *spec = codec->spec; - return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); -} -#endif - - -/* - */ -static struct hda_codec_ops generic_patch_ops = { - .build_controls = build_generic_controls, - .build_pcms = build_generic_pcms, - .free = snd_hda_generic_free, -#ifdef CONFIG_SND_HDA_POWER_SAVE - .check_power_status = generic_check_power_status, -#endif -}; - -/* - * the generic parser - */ -int snd_hda_parse_generic_codec(struct hda_codec *codec) -{ - struct hda_gspec *spec; - int err; - - if(!codec->afg) - return 0; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) { - printk(KERN_ERR "hda_generic: can't allocate spec\n"); - return -ENOMEM; - } - codec->spec = spec; - INIT_LIST_HEAD(&spec->nid_list); - - if ((err = build_afg_tree(codec)) < 0) - goto error; - - if ((err = parse_input(codec)) < 0 || - (err = parse_output(codec)) < 0) - goto error; - - codec->patch_ops = generic_patch_ops; - - return 0; - - error: - snd_hda_generic_free(codec); - return err; -} -EXPORT_SYMBOL(snd_hda_parse_generic_codec); diff --git a/ANDROID_3.4.5/sound/pci/hda/hda_hwdep.c b/ANDROID_3.4.5/sound/pci/hda/hda_hwdep.c deleted file mode 100644 index 6b2efb8c..00000000 --- a/ANDROID_3.4.5/sound/pci/hda/hda_hwdep.c +++ /dev/null @@ -1,817 +0,0 @@ -/* - * HWDEP Interface for HD-audio codec - * - * Copyright (c) 2007 Takashi Iwai <tiwai@suse.de> - * - * 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 <linux/slab.h> -#include <linux/pci.h> -#include <linux/compat.h> -#include <linux/mutex.h> -#include <linux/ctype.h> -#include <linux/string.h> -#include <linux/firmware.h> -#include <linux/export.h> -#include <sound/core.h> -#include "hda_codec.h" -#include "hda_local.h" -#include <sound/hda_hwdep.h> -#include <sound/minors.h> - -/* hint string pair */ -struct hda_hint { - const char *key; - const char *val; /* contained in the same alloc as key */ -}; - -/* - * write/read an out-of-bound verb - */ -static int verb_write_ioctl(struct hda_codec *codec, - struct hda_verb_ioctl __user *arg) -{ - u32 verb, res; - - if (get_user(verb, &arg->verb)) - return -EFAULT; - res = snd_hda_codec_read(codec, verb >> 24, 0, - (verb >> 8) & 0xffff, verb & 0xff); - if (put_user(res, &arg->res)) - return -EFAULT; - return 0; -} - -static int get_wcap_ioctl(struct hda_codec *codec, - struct hda_verb_ioctl __user *arg) -{ - u32 verb, res; - - if (get_user(verb, &arg->verb)) - return -EFAULT; - res = get_wcaps(codec, verb >> 24); - if (put_user(res, &arg->res)) - return -EFAULT; - return 0; -} - - -/* - */ -static int hda_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct hda_codec *codec = hw->private_data; - void __user *argp = (void __user *)arg; - - switch (cmd) { - case HDA_IOCTL_PVERSION: - return put_user(HDA_HWDEP_VERSION, (int __user *)argp); - case HDA_IOCTL_VERB_WRITE: - return verb_write_ioctl(codec, argp); - case HDA_IOCTL_GET_WCAP: - return get_wcap_ioctl(codec, argp); - } - return -ENOIOCTLCMD; -} - -#ifdef CONFIG_COMPAT -static int hda_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file, - unsigned int cmd, unsigned long arg) -{ - return hda_hwdep_ioctl(hw, file, cmd, (unsigned long)compat_ptr(arg)); -} -#endif - -static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file) -{ -#ifndef CONFIG_SND_DEBUG_VERBOSE - if (!capable(CAP_SYS_RAWIO)) - return -EACCES; -#endif - return 0; -} - -static void clear_hwdep_elements(struct hda_codec *codec) -{ - int i; - - /* clear init verbs */ - snd_array_free(&codec->init_verbs); - /* clear hints */ - for (i = 0; i < codec->hints.used; i++) { - struct hda_hint *hint = snd_array_elem(&codec->hints, i); - kfree(hint->key); /* we don't need to free hint->val */ - } - snd_array_free(&codec->hints); - snd_array_free(&codec->user_pins); -} - -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; - int err; - - sprintf(hwname, "HDA Codec %d", codec->addr); - err = snd_hwdep_new(codec->bus->card, hwname, codec->addr, &hwdep); - if (err < 0) - return err; - codec->hwdep = hwdep; - 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; - hwdep->ops.ioctl = hda_hwdep_ioctl; -#ifdef CONFIG_COMPAT - 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(struct hda_hint), 32); - snd_array_init(&codec->user_pins, sizeof(struct hda_pincfg), 16); - - return 0; -} - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static ssize_t power_on_acct_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct snd_hwdep *hwdep = dev_get_drvdata(dev); - struct hda_codec *codec = hwdep->private_data; - snd_hda_update_power_acct(codec); - return sprintf(buf, "%u\n", jiffies_to_msecs(codec->power_on_acct)); -} - -static ssize_t power_off_acct_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct snd_hwdep *hwdep = dev_get_drvdata(dev); - struct hda_codec *codec = hwdep->private_data; - snd_hda_update_power_acct(codec); - return sprintf(buf, "%u\n", jiffies_to_msecs(codec->power_off_acct)); -} - -static struct device_attribute power_attrs[] = { - __ATTR_RO(power_on_acct), - __ATTR_RO(power_off_acct), -}; - -int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec) -{ - struct snd_hwdep *hwdep = codec->hwdep; - int i; - - for (i = 0; i < ARRAY_SIZE(power_attrs); i++) - snd_add_device_sysfs_file(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, - hwdep->device, &power_attrs[i]); - return 0; -} -#endif /* CONFIG_SND_HDA_POWER_SAVE */ - -#ifdef CONFIG_SND_HDA_RECONFIG - -/* - * sysfs interface - */ - -static int clear_codec(struct hda_codec *codec) -{ - int err; - - err = snd_hda_codec_reset(codec); - if (err < 0) { - snd_printk(KERN_ERR "The codec is being used, can't free.\n"); - return err; - } - clear_hwdep_elements(codec); - return 0; -} - -static int reconfig_codec(struct hda_codec *codec) -{ - int err; - - snd_hda_power_up(codec); - snd_printk(KERN_INFO "hda-codec: reconfiguring\n"); - err = snd_hda_codec_reset(codec); - if (err < 0) { - snd_printk(KERN_ERR - "The codec is being used, can't reconfigure.\n"); - goto error; - } - err = snd_hda_codec_configure(codec); - if (err < 0) - goto error; - /* rebuild PCMs */ - err = snd_hda_codec_build_pcms(codec); - if (err < 0) - goto error; - /* rebuild mixers */ - err = snd_hda_codec_build_controls(codec); - if (err < 0) - goto error; - err = snd_card_register(codec->bus->card); - error: - snd_hda_power_down(codec); - return err; -} - -/* - * 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(vendor_name); -CODEC_INFO_STR_SHOW(chip_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; \ - unsigned long val; \ - int err = strict_strtoul(buf, 0, &val); \ - if (err < 0) \ - return err; \ - codec->type = val; \ - 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(vendor_name); -CODEC_INFO_STR_STORE(chip_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_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; - int i, len = 0; - for (i = 0; i < codec->init_verbs.used; i++) { - struct hda_verb *v = snd_array_elem(&codec->init_verbs, i); - len += snprintf(buf + len, PAGE_SIZE - len, - "0x%02x 0x%03x 0x%04x\n", - v->nid, v->verb, v->param); - } - return len; -} - -static int parse_init_verbs(struct hda_codec *codec, const char *buf) -{ - struct hda_verb *v; - int nid, verb, param; - - if (sscanf(buf, "%i %i %i", &nid, &verb, ¶m) != 3) - return -EINVAL; - if (!nid || !verb) - return -EINVAL; - v = snd_array_new(&codec->init_verbs); - if (!v) - return -ENOMEM; - v->nid = nid; - v->verb = verb; - v->param = param; - return 0; -} - -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; - int err = parse_init_verbs(codec, buf); - if (err < 0) - return err; - return count; -} - -static ssize_t hints_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; - int i, len = 0; - for (i = 0; i < codec->hints.used; i++) { - struct hda_hint *hint = snd_array_elem(&codec->hints, i); - len += snprintf(buf + len, PAGE_SIZE - len, - "%s = %s\n", hint->key, hint->val); - } - return len; -} - -static struct hda_hint *get_hint(struct hda_codec *codec, const char *key) -{ - int i; - - for (i = 0; i < codec->hints.used; i++) { - struct hda_hint *hint = snd_array_elem(&codec->hints, i); - if (!strcmp(hint->key, key)) - return hint; - } - return NULL; -} - -static void remove_trail_spaces(char *str) -{ - char *p; - if (!*str) - return; - p = str + strlen(str) - 1; - for (; isspace(*p); p--) { - *p = 0; - if (p == str) - return; - } -} - -#define MAX_HINTS 1024 - -static int parse_hints(struct hda_codec *codec, const char *buf) -{ - char *key, *val; - struct hda_hint *hint; - - buf = skip_spaces(buf); - if (!*buf || *buf == '#' || *buf == '\n') - return 0; - if (*buf == '=') - return -EINVAL; - key = kstrndup_noeol(buf, 1024); - if (!key) - return -ENOMEM; - /* extract key and val */ - val = strchr(key, '='); - if (!val) { - kfree(key); - return -EINVAL; - } - *val++ = 0; - val = skip_spaces(val); - remove_trail_spaces(key); - remove_trail_spaces(val); - hint = get_hint(codec, key); - if (hint) { - /* replace */ - kfree(hint->key); - hint->key = key; - hint->val = val; - return 0; - } - /* allocate a new hint entry */ - if (codec->hints.used >= MAX_HINTS) - hint = NULL; - else - hint = snd_array_new(&codec->hints); - if (!hint) { - kfree(key); - return -ENOMEM; - } - hint->key = key; - hint->val = val; - return 0; -} - -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; - int err = parse_hints(codec, buf); - if (err < 0) - return err; - return count; -} - -static ssize_t pin_configs_show(struct hda_codec *codec, - struct snd_array *list, - char *buf) -{ - int i, len = 0; - for (i = 0; i < list->used; i++) { - struct hda_pincfg *pin = snd_array_elem(list, i); - len += sprintf(buf + len, "0x%02x 0x%08x\n", - pin->nid, pin->cfg); - } - return len; -} - -static ssize_t init_pin_configs_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 pin_configs_show(codec, &codec->init_pins, buf); -} - -static ssize_t user_pin_configs_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 pin_configs_show(codec, &codec->user_pins, buf); -} - -static ssize_t driver_pin_configs_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 pin_configs_show(codec, &codec->driver_pins, buf); -} - -#define MAX_PIN_CONFIGS 32 - -static int parse_user_pin_configs(struct hda_codec *codec, const char *buf) -{ - int nid, cfg; - - if (sscanf(buf, "%i %i", &nid, &cfg) != 2) - return -EINVAL; - if (!nid) - return -EINVAL; - return snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg); -} - -static ssize_t user_pin_configs_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 = parse_user_pin_configs(codec, buf); - if (err < 0) - return err; - 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(vendor_name), - CODEC_ATTR_RW(chip_name), - CODEC_ATTR_RW(modelname), - CODEC_ATTR_RW(init_verbs), - CODEC_ATTR_RW(hints), - CODEC_ATTR_RO(init_pin_configs), - CODEC_ATTR_RW(user_pin_configs), - CODEC_ATTR_RO(driver_pin_configs), - 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; -} - -/* - * Look for hint string - */ -const char *snd_hda_get_hint(struct hda_codec *codec, const char *key) -{ - struct hda_hint *hint = get_hint(codec, key); - return hint ? hint->val : NULL; -} -EXPORT_SYMBOL_HDA(snd_hda_get_hint); - -int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key) -{ - const char *p = snd_hda_get_hint(codec, key); - if (!p || !*p) - return -ENOENT; - switch (toupper(*p)) { - case 'T': /* true */ - case 'Y': /* yes */ - case '1': - return 1; - } - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_get_bool_hint); - -#endif /* CONFIG_SND_HDA_RECONFIG */ - -#ifdef CONFIG_SND_HDA_PATCH_LOADER - -/* parser mode */ -enum { - LINE_MODE_NONE, - LINE_MODE_CODEC, - LINE_MODE_MODEL, - LINE_MODE_PINCFG, - LINE_MODE_VERB, - LINE_MODE_HINT, - LINE_MODE_VENDOR_ID, - LINE_MODE_SUBSYSTEM_ID, - LINE_MODE_REVISION_ID, - LINE_MODE_CHIP_NAME, - NUM_LINE_MODES, -}; - -static inline int strmatch(const char *a, const char *b) -{ - return strnicmp(a, b, strlen(b)) == 0; -} - -/* parse the contents after the line "[codec]" - * accept only the line with three numbers, and assign the current codec - */ -static void parse_codec_mode(char *buf, struct hda_bus *bus, - struct hda_codec **codecp) -{ - int vendorid, subid, caddr; - struct hda_codec *codec; - - *codecp = NULL; - if (sscanf(buf, "%i %i %i", &vendorid, &subid, &caddr) == 3) { - list_for_each_entry(codec, &bus->codec_list, list) { - if ((vendorid <= 0 || codec->vendor_id == vendorid) && - (subid <= 0 || codec->subsystem_id == subid) && - codec->addr == caddr) { - *codecp = codec; - break; - } - } - } -} - -/* parse the contents after the other command tags, [pincfg], [verb], - * [vendor_id], [subsystem_id], [revision_id], [chip_name], [hint] and [model] - * just pass to the sysfs helper (only when any codec was specified) - */ -static void parse_pincfg_mode(char *buf, struct hda_bus *bus, - struct hda_codec **codecp) -{ - parse_user_pin_configs(*codecp, buf); -} - -static void parse_verb_mode(char *buf, struct hda_bus *bus, - struct hda_codec **codecp) -{ - parse_init_verbs(*codecp, buf); -} - -static void parse_hint_mode(char *buf, struct hda_bus *bus, - struct hda_codec **codecp) -{ - parse_hints(*codecp, buf); -} - -static void parse_model_mode(char *buf, struct hda_bus *bus, - struct hda_codec **codecp) -{ - kfree((*codecp)->modelname); - (*codecp)->modelname = kstrdup(buf, GFP_KERNEL); -} - -static void parse_chip_name_mode(char *buf, struct hda_bus *bus, - struct hda_codec **codecp) -{ - kfree((*codecp)->chip_name); - (*codecp)->chip_name = kstrdup(buf, GFP_KERNEL); -} - -#define DEFINE_PARSE_ID_MODE(name) \ -static void parse_##name##_mode(char *buf, struct hda_bus *bus, \ - struct hda_codec **codecp) \ -{ \ - unsigned long val; \ - if (!strict_strtoul(buf, 0, &val)) \ - (*codecp)->name = val; \ -} - -DEFINE_PARSE_ID_MODE(vendor_id); -DEFINE_PARSE_ID_MODE(subsystem_id); -DEFINE_PARSE_ID_MODE(revision_id); - - -struct hda_patch_item { - const char *tag; - void (*parser)(char *buf, struct hda_bus *bus, struct hda_codec **retc); - int need_codec; -}; - -static struct hda_patch_item patch_items[NUM_LINE_MODES] = { - [LINE_MODE_CODEC] = { "[codec]", parse_codec_mode, 0 }, - [LINE_MODE_MODEL] = { "[model]", parse_model_mode, 1 }, - [LINE_MODE_VERB] = { "[verb]", parse_verb_mode, 1 }, - [LINE_MODE_PINCFG] = { "[pincfg]", parse_pincfg_mode, 1 }, - [LINE_MODE_HINT] = { "[hint]", parse_hint_mode, 1 }, - [LINE_MODE_VENDOR_ID] = { "[vendor_id]", parse_vendor_id_mode, 1 }, - [LINE_MODE_SUBSYSTEM_ID] = { "[subsystem_id]", parse_subsystem_id_mode, 1 }, - [LINE_MODE_REVISION_ID] = { "[revision_id]", parse_revision_id_mode, 1 }, - [LINE_MODE_CHIP_NAME] = { "[chip_name]", parse_chip_name_mode, 1 }, -}; - -/* check the line starting with '[' -- change the parser mode accodingly */ -static int parse_line_mode(char *buf, struct hda_bus *bus) -{ - int i; - for (i = 0; i < ARRAY_SIZE(patch_items); i++) { - if (!patch_items[i].tag) - continue; - if (strmatch(buf, patch_items[i].tag)) - return i; - } - return LINE_MODE_NONE; -} - -/* copy one line from the buffer in fw, and update the fields in fw - * return zero if it reaches to the end of the buffer, or non-zero - * if successfully copied a line - * - * the spaces at the beginning and the end of the line are stripped - */ -static int get_line_from_fw(char *buf, int size, struct firmware *fw) -{ - int len; - const char *p = fw->data; - while (isspace(*p) && fw->size) { - p++; - fw->size--; - } - if (!fw->size) - return 0; - - for (len = 0; len < fw->size; len++) { - if (!*p) - break; - if (*p == '\n') { - p++; - len++; - break; - } - if (len < size) - *buf++ = *p++; - } - *buf = 0; - fw->size -= len; - fw->data = p; - remove_trail_spaces(buf); - return 1; -} - -/* - * load a "patch" firmware file and parse it - */ -int snd_hda_load_patch(struct hda_bus *bus, const char *patch) -{ - int err; - const struct firmware *fw; - struct firmware tmp; - char buf[128]; - struct hda_codec *codec; - int line_mode; - struct device *dev = bus->card->dev; - - if (snd_BUG_ON(!dev)) - return -ENODEV; - err = request_firmware(&fw, patch, dev); - if (err < 0) { - printk(KERN_ERR "hda-codec: Cannot load the patch '%s'\n", - patch); - return err; - } - - tmp = *fw; - line_mode = LINE_MODE_NONE; - codec = NULL; - while (get_line_from_fw(buf, sizeof(buf) - 1, &tmp)) { - if (!*buf || *buf == '#' || *buf == '\n') - continue; - if (*buf == '[') - line_mode = parse_line_mode(buf, bus); - else if (patch_items[line_mode].parser && - (codec || !patch_items[line_mode].need_codec)) - patch_items[line_mode].parser(buf, bus, &codec); - } - release_firmware(fw); - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_load_patch); -#endif /* CONFIG_SND_HDA_PATCH_LOADER */ diff --git a/ANDROID_3.4.5/sound/pci/hda/hda_intel.c b/ANDROID_3.4.5/sound/pci/hda/hda_intel.c deleted file mode 100644 index 1f350522..00000000 --- a/ANDROID_3.4.5/sound/pci/hda/hda_intel.c +++ /dev/null @@ -1,3173 +0,0 @@ -/* - * - * hda_intel.c - Implementation of primary alsa driver code base - * for Intel HD Audio. - * - * Copyright(c) 2004 Intel Corporation. All rights reserved. - * - * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> - * PeiSen Hou <pshou@realtek.com.tw> - * - * 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. - * - * CONTACTS: - * - * Matt Jared matt.jared@intel.com - * Andy Kopp andy.kopp@intel.com - * Dan Kogan dan.d.kogan@intel.com - * - * CHANGES: - * - * 2004.12.01 Major rewrite by tiwai, merged the work of pshou - * - */ - -#include <linux/delay.h> -#include <linux/interrupt.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/dma-mapping.h> -#include <linux/moduleparam.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/pci.h> -#include <linux/mutex.h> -#include <linux/reboot.h> -#include <linux/io.h> -#ifdef CONFIG_X86 -/* for snoop control */ -#include <asm/pgtable.h> -#include <asm/cacheflush.h> -#endif -#include <sound/core.h> -#include <sound/initval.h> -#include "hda_codec.h" - - -static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; -static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; -static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; -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 bool single_cmd; -static int enable_msi = -1; -#ifdef CONFIG_SND_HDA_PATCH_LOADER -static char *patch[SNDRV_CARDS]; -#endif -#ifdef CONFIG_SND_HDA_INPUT_BEEP -static int beep_mode[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = - CONFIG_SND_HDA_INPUT_BEEP_MODE}; -#endif - -module_param_array(index, int, NULL, 0444); -MODULE_PARM_DESC(index, "Index value for Intel HD audio interface."); -module_param_array(id, charp, NULL, 0444); -MODULE_PARM_DESC(id, "ID string for Intel HD audio interface."); -module_param_array(enable, bool, NULL, 0444); -MODULE_PARM_DESC(enable, "Enable Intel HD audio interface."); -module_param_array(model, charp, NULL, 0444); -MODULE_PARM_DESC(model, "Use the given board model."); -module_param_array(position_fix, int, NULL, 0444); -MODULE_PARM_DESC(position_fix, "DMA pointer read method." - "(0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO, 4 = COMBO)."); -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, int, 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)."); -module_param(enable_msi, bint, 0444); -MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)"); -#ifdef CONFIG_SND_HDA_PATCH_LOADER -module_param_array(patch, charp, NULL, 0444); -MODULE_PARM_DESC(patch, "Patch file for Intel HD audio interface."); -#endif -#ifdef CONFIG_SND_HDA_INPUT_BEEP -module_param_array(beep_mode, int, NULL, 0444); -MODULE_PARM_DESC(beep_mode, "Select HDA Beep registration mode " - "(0=off, 1=on, 2=mute switch on/off) (default=1)."); -#endif - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT; -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 - * wake up. - */ -static bool power_save_controller = 1; -module_param(power_save_controller, bool, 0644); -MODULE_PARM_DESC(power_save_controller, "Reset controller in power save mode."); -#endif - -static int align_buffer_size = -1; -module_param(align_buffer_size, bint, 0644); -MODULE_PARM_DESC(align_buffer_size, - "Force buffer and period sizes to be multiple of 128 bytes."); - -#ifdef CONFIG_X86 -static bool hda_snoop = true; -module_param_named(snoop, hda_snoop, bool, 0444); -MODULE_PARM_DESC(snoop, "Enable/disable snooping"); -#define azx_snoop(chip) (chip)->snoop -#else -#define hda_snoop true -#define azx_snoop(chip) true -#endif - - -MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("{{Intel, ICH6}," - "{Intel, ICH6M}," - "{Intel, ICH7}," - "{Intel, ESB2}," - "{Intel, ICH8}," - "{Intel, ICH9}," - "{Intel, ICH10}," - "{Intel, PCH}," - "{Intel, CPT}," - "{Intel, PPT}," - "{Intel, LPT}," - "{Intel, PBG}," - "{Intel, SCH}," - "{ATI, SB450}," - "{ATI, SB600}," - "{ATI, RS600}," - "{ATI, RS690}," - "{ATI, RS780}," - "{ATI, R600}," - "{ATI, RV630}," - "{ATI, RV610}," - "{ATI, RV670}," - "{ATI, RV635}," - "{ATI, RV620}," - "{ATI, RV770}," - "{VIA, VT8251}," - "{VIA, VT8237A}," - "{SiS, SIS966}," - "{ULI, M5461}}"); -MODULE_DESCRIPTION("Intel HDA driver"); - -#ifdef CONFIG_SND_VERBOSE_PRINTK -#define SFX /* nop */ -#else -#define SFX "hda-intel: " -#endif - -/* - * registers - */ -#define ICH6_REG_GCAP 0x00 -#define ICH6_GCAP_64OK (1 << 0) /* 64bit address support */ -#define ICH6_GCAP_NSDO (3 << 1) /* # of serial data out signals */ -#define ICH6_GCAP_BSS (31 << 3) /* # of bidirectional streams */ -#define ICH6_GCAP_ISS (15 << 8) /* # of input streams */ -#define ICH6_GCAP_OSS (15 << 12) /* # of output streams */ -#define ICH6_REG_VMIN 0x02 -#define ICH6_REG_VMAJ 0x03 -#define ICH6_REG_OUTPAY 0x04 -#define ICH6_REG_INPAY 0x06 -#define ICH6_REG_GCTL 0x08 -#define ICH6_GCTL_RESET (1 << 0) /* controller reset */ -#define ICH6_GCTL_FCNTRL (1 << 1) /* flush control */ -#define ICH6_GCTL_UNSOL (1 << 8) /* accept unsol. response enable */ -#define ICH6_REG_WAKEEN 0x0c -#define ICH6_REG_STATESTS 0x0e -#define ICH6_REG_GSTS 0x10 -#define ICH6_GSTS_FSTS (1 << 1) /* flush status */ -#define ICH6_REG_INTCTL 0x20 -#define ICH6_REG_INTSTS 0x24 -#define ICH6_REG_WALLCLK 0x30 /* 24Mhz source */ -#define ICH6_REG_OLD_SSYNC 0x34 /* SSYNC for old ICH */ -#define ICH6_REG_SSYNC 0x38 -#define ICH6_REG_CORBLBASE 0x40 -#define ICH6_REG_CORBUBASE 0x44 -#define ICH6_REG_CORBWP 0x48 -#define ICH6_REG_CORBRP 0x4a -#define ICH6_CORBRP_RST (1 << 15) /* read pointer reset */ -#define ICH6_REG_CORBCTL 0x4c -#define ICH6_CORBCTL_RUN (1 << 1) /* enable DMA */ -#define ICH6_CORBCTL_CMEIE (1 << 0) /* enable memory error irq */ -#define ICH6_REG_CORBSTS 0x4d -#define ICH6_CORBSTS_CMEI (1 << 0) /* memory error indication */ -#define ICH6_REG_CORBSIZE 0x4e - -#define ICH6_REG_RIRBLBASE 0x50 -#define ICH6_REG_RIRBUBASE 0x54 -#define ICH6_REG_RIRBWP 0x58 -#define ICH6_RIRBWP_RST (1 << 15) /* write pointer reset */ -#define ICH6_REG_RINTCNT 0x5a -#define ICH6_REG_RIRBCTL 0x5c -#define ICH6_RBCTL_IRQ_EN (1 << 0) /* enable IRQ */ -#define ICH6_RBCTL_DMA_EN (1 << 1) /* enable DMA */ -#define ICH6_RBCTL_OVERRUN_EN (1 << 2) /* enable overrun irq */ -#define ICH6_REG_RIRBSTS 0x5d -#define ICH6_RBSTS_IRQ (1 << 0) /* response irq */ -#define ICH6_RBSTS_OVERRUN (1 << 2) /* overrun irq */ -#define ICH6_REG_RIRBSIZE 0x5e - -#define ICH6_REG_IC 0x60 -#define ICH6_REG_IR 0x64 -#define ICH6_REG_IRS 0x68 -#define ICH6_IRS_VALID (1<<1) -#define ICH6_IRS_BUSY (1<<0) - -#define ICH6_REG_DPLBASE 0x70 -#define ICH6_REG_DPUBASE 0x74 -#define ICH6_DPLBASE_ENABLE 0x1 /* Enable position buffer */ - -/* SD offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */ -enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; - -/* stream register offsets from stream base */ -#define ICH6_REG_SD_CTL 0x00 -#define ICH6_REG_SD_STS 0x03 -#define ICH6_REG_SD_LPIB 0x04 -#define ICH6_REG_SD_CBL 0x08 -#define ICH6_REG_SD_LVI 0x0c -#define ICH6_REG_SD_FIFOW 0x0e -#define ICH6_REG_SD_FIFOSIZE 0x10 -#define ICH6_REG_SD_FORMAT 0x12 -#define ICH6_REG_SD_BDLPL 0x18 -#define ICH6_REG_SD_BDLPU 0x1c - -/* PCI space */ -#define ICH6_PCIREG_TCSEL 0x44 - -/* - * other constants - */ - -/* max number of SDs */ -/* ICH, ATI and VIA have 4 playback and 4 capture */ -#define ICH6_NUM_CAPTURE 4 -#define ICH6_NUM_PLAYBACK 4 - -/* ULI has 6 playback and 5 capture */ -#define ULI_NUM_CAPTURE 5 -#define ULI_NUM_PLAYBACK 6 - -/* ATI HDMI has 1 playback and 0 capture */ -#define ATIHDMI_NUM_CAPTURE 0 -#define ATIHDMI_NUM_PLAYBACK 1 - -/* TERA has 4 playback and 3 capture */ -#define TERA_NUM_CAPTURE 3 -#define TERA_NUM_PLAYBACK 4 - -/* this number is statically defined for simplicity */ -#define MAX_AZX_DEV 16 - -/* max number of fragments - we may use more if allocating more pages for BDL */ -#define BDL_SIZE 4096 -#define AZX_MAX_BDL_ENTRIES (BDL_SIZE / 16) -#define AZX_MAX_FRAG 32 -/* max buffer size - no h/w limit, you can increase as you like */ -#define AZX_MAX_BUF_SIZE (1024*1024*1024) - -/* RIRB int mask: overrun[2], response[0] */ -#define RIRB_INT_RESPONSE 0x01 -#define RIRB_INT_OVERRUN 0x04 -#define RIRB_INT_MASK 0x05 - -/* STATESTS int mask: S3,SD2,SD1,SD0 */ -#define AZX_MAX_CODECS 8 -#define AZX_DEFAULT_CODECS 4 -#define STATESTS_INT_MASK ((1 << AZX_MAX_CODECS) - 1) - -/* SD_CTL bits */ -#define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */ -#define SD_CTL_DMA_START 0x02 /* stream DMA start bit */ -#define SD_CTL_STRIPE (3 << 16) /* stripe control */ -#define SD_CTL_TRAFFIC_PRIO (1 << 18) /* traffic priority */ -#define SD_CTL_DIR (1 << 19) /* bi-directional stream */ -#define SD_CTL_STREAM_TAG_MASK (0xf << 20) -#define SD_CTL_STREAM_TAG_SHIFT 20 - -/* SD_CTL and SD_STS */ -#define SD_INT_DESC_ERR 0x10 /* descriptor error interrupt */ -#define SD_INT_FIFO_ERR 0x08 /* FIFO error interrupt */ -#define SD_INT_COMPLETE 0x04 /* completion interrupt */ -#define SD_INT_MASK (SD_INT_DESC_ERR|SD_INT_FIFO_ERR|\ - SD_INT_COMPLETE) - -/* SD_STS */ -#define SD_STS_FIFO_READY 0x20 /* FIFO ready */ - -/* INTCTL and INTSTS */ -#define ICH6_INT_ALL_STREAM 0xff /* all stream interrupts */ -#define ICH6_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */ -#define ICH6_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */ - -/* below are so far hardcoded - should read registers in future */ -#define ICH6_MAX_CORB_ENTRIES 256 -#define ICH6_MAX_RIRB_ENTRIES 256 - -/* position fix mode */ -enum { - POS_FIX_AUTO, - POS_FIX_LPIB, - POS_FIX_POSBUF, - POS_FIX_VIACOMBO, - POS_FIX_COMBO, -}; - -/* Defines for ATI HD Audio support in SB450 south bridge */ -#define ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR 0x42 -#define ATI_SB450_HDAUDIO_ENABLE_SNOOP 0x02 - -/* Defines for Nvidia HDA support */ -#define NVIDIA_HDA_TRANSREG_ADDR 0x4e -#define NVIDIA_HDA_ENABLE_COHBITS 0x0f -#define NVIDIA_HDA_ISTRM_COH 0x4d -#define NVIDIA_HDA_OSTRM_COH 0x4c -#define NVIDIA_HDA_ENABLE_COHBIT 0x01 - -/* Defines for Intel SCH HDA snoop control */ -#define INTEL_SCH_HDA_DEVC 0x78 -#define INTEL_SCH_HDA_DEVC_NOSNOOP (0x1<<11) - -/* Define IN stream 0 FIFO size offset in VIA controller */ -#define VIA_IN_STREAM0_FIFO_SIZE_OFFSET 0x90 -/* Define VIA HD Audio Device ID*/ -#define VIA_HDAC_DEVICE_ID 0x3288 - -/* HD Audio class code */ -#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403 - -/* - */ - -struct azx_dev { - struct snd_dma_buffer bdl; /* BDL buffer */ - u32 *posbuf; /* position buffer pointer */ - - unsigned int bufsize; /* size of the play buffer in bytes */ - unsigned int period_bytes; /* size of the period in bytes */ - unsigned int frags; /* number for period in the play buffer */ - unsigned int fifo_size; /* FIFO size */ - unsigned long start_wallclk; /* start + minimum wallclk */ - unsigned long period_wallclk; /* wallclk for period */ - - void __iomem *sd_addr; /* stream descriptor pointer */ - - u32 sd_int_sta_mask; /* stream int status mask */ - - /* pcm support */ - struct snd_pcm_substream *substream; /* assigned substream, - * set in PCM open - */ - unsigned int format_val; /* format value to be set in the - * controller and the codec - */ - unsigned char stream_tag; /* assigned stream */ - unsigned char index; /* stream index */ - int assigned_key; /* last device# key assigned to */ - - unsigned int opened :1; - unsigned int running :1; - unsigned int irq_pending :1; - /* - * For VIA: - * A flag to ensure DMA position is 0 - * when link position is not greater than FIFO size - */ - unsigned int insufficient :1; - unsigned int wc_marked:1; -}; - -/* CORB/RIRB */ -struct azx_rb { - u32 *buf; /* CORB/RIRB buffer - * Each CORB entry is 4byte, RIRB is 8byte - */ - dma_addr_t addr; /* physical address of CORB/RIRB buffer */ - /* for RIRB */ - unsigned short rp, wp; /* read/write pointers */ - int cmds[AZX_MAX_CODECS]; /* number of pending requests */ - u32 res[AZX_MAX_CODECS]; /* last read value */ -}; - -struct azx_pcm { - struct azx *chip; - struct snd_pcm *pcm; - struct hda_codec *codec; - struct hda_pcm_stream *hinfo[2]; - struct list_head list; -}; - -struct azx { - struct snd_card *card; - struct pci_dev *pci; - int dev_index; - - /* chip type specific */ - int driver_type; - unsigned int driver_caps; - int playback_streams; - int playback_index_offset; - int capture_streams; - int capture_index_offset; - int num_streams; - - /* pci resources */ - unsigned long addr; - void __iomem *remap_addr; - int irq; - - /* locks */ - spinlock_t reg_lock; - struct mutex open_mutex; - - /* streams (x num_streams) */ - struct azx_dev *azx_dev; - - /* PCM */ - struct list_head pcm_list; /* azx_pcm list */ - - /* HD codec */ - unsigned short codec_mask; - int codec_probe_mask; /* copied from probe_mask option */ - struct hda_bus *bus; - unsigned int beep_mode; - - /* CORB/RIRB */ - struct azx_rb corb; - struct azx_rb rirb; - - /* CORB/RIRB and position buffers */ - struct snd_dma_buffer rb; - struct snd_dma_buffer posbuf; - - /* flags */ - int position_fix[2]; /* for both playback/capture streams */ - int poll_count; - unsigned int running :1; - unsigned int initialized :1; - unsigned int single_cmd :1; - unsigned int polling_mode :1; - unsigned int msi :1; - unsigned int irq_pending_warned :1; - unsigned int probing :1; /* codec probing phase */ - unsigned int snoop:1; - unsigned int align_buffer_size:1; - - /* for debugging */ - unsigned int last_cmd[AZX_MAX_CODECS]; - - /* 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 */ -enum { - AZX_DRIVER_ICH, - AZX_DRIVER_PCH, - AZX_DRIVER_SCH, - AZX_DRIVER_ATI, - AZX_DRIVER_ATIHDMI, - AZX_DRIVER_ATIHDMI_NS, - AZX_DRIVER_VIA, - AZX_DRIVER_SIS, - AZX_DRIVER_ULI, - AZX_DRIVER_NVIDIA, - AZX_DRIVER_TERA, - AZX_DRIVER_CTX, - AZX_DRIVER_GENERIC, - AZX_NUM_DRIVERS, /* keep this as last entry */ -}; - -/* driver quirks (capabilities) */ -/* bits 0-7 are used for indicating driver type */ -#define AZX_DCAPS_NO_TCSEL (1 << 8) /* No Intel TCSEL bit */ -#define AZX_DCAPS_NO_MSI (1 << 9) /* No MSI support */ -#define AZX_DCAPS_ATI_SNOOP (1 << 10) /* ATI snoop enable */ -#define AZX_DCAPS_NVIDIA_SNOOP (1 << 11) /* Nvidia snoop enable */ -#define AZX_DCAPS_SCH_SNOOP (1 << 12) /* SCH/PCH snoop enable */ -#define AZX_DCAPS_RIRB_DELAY (1 << 13) /* Long delay in read loop */ -#define AZX_DCAPS_RIRB_PRE_DELAY (1 << 14) /* Put a delay before read */ -#define AZX_DCAPS_CTX_WORKAROUND (1 << 15) /* X-Fi workaround */ -#define AZX_DCAPS_POSFIX_LPIB (1 << 16) /* Use LPIB as default */ -#define AZX_DCAPS_POSFIX_VIA (1 << 17) /* Use VIACOMBO as default */ -#define AZX_DCAPS_NO_64BIT (1 << 18) /* No 64bit address */ -#define AZX_DCAPS_SYNC_WRITE (1 << 19) /* sync each cmd write */ -#define AZX_DCAPS_OLD_SSYNC (1 << 20) /* Old SSYNC reg for ICH */ -#define AZX_DCAPS_BUFSIZE (1 << 21) /* no buffer size alignment */ -#define AZX_DCAPS_ALIGN_BUFSIZE (1 << 22) /* buffer size alignment */ - -/* quirks for ATI SB / AMD Hudson */ -#define AZX_DCAPS_PRESET_ATI_SB \ - (AZX_DCAPS_ATI_SNOOP | AZX_DCAPS_NO_TCSEL | \ - AZX_DCAPS_SYNC_WRITE | AZX_DCAPS_POSFIX_LPIB) - -/* quirks for ATI/AMD HDMI */ -#define AZX_DCAPS_PRESET_ATI_HDMI \ - (AZX_DCAPS_NO_TCSEL | AZX_DCAPS_SYNC_WRITE | AZX_DCAPS_POSFIX_LPIB) - -/* quirks for Nvidia */ -#define AZX_DCAPS_PRESET_NVIDIA \ - (AZX_DCAPS_NVIDIA_SNOOP | AZX_DCAPS_RIRB_DELAY | AZX_DCAPS_NO_MSI |\ - AZX_DCAPS_ALIGN_BUFSIZE) - -static char *driver_short_names[] __devinitdata = { - [AZX_DRIVER_ICH] = "HDA Intel", - [AZX_DRIVER_PCH] = "HDA Intel PCH", - [AZX_DRIVER_SCH] = "HDA Intel MID", - [AZX_DRIVER_ATI] = "HDA ATI SB", - [AZX_DRIVER_ATIHDMI] = "HDA ATI HDMI", - [AZX_DRIVER_ATIHDMI_NS] = "HDA ATI HDMI", - [AZX_DRIVER_VIA] = "HDA VIA VT82xx", - [AZX_DRIVER_SIS] = "HDA SIS966", - [AZX_DRIVER_ULI] = "HDA ULI M5461", - [AZX_DRIVER_NVIDIA] = "HDA NVidia", - [AZX_DRIVER_TERA] = "HDA Teradici", - [AZX_DRIVER_CTX] = "HDA Creative", - [AZX_DRIVER_GENERIC] = "HD-Audio Generic", -}; - -/* - * macros for easy use - */ -#define azx_writel(chip,reg,value) \ - writel(value, (chip)->remap_addr + ICH6_REG_##reg) -#define azx_readl(chip,reg) \ - readl((chip)->remap_addr + ICH6_REG_##reg) -#define azx_writew(chip,reg,value) \ - writew(value, (chip)->remap_addr + ICH6_REG_##reg) -#define azx_readw(chip,reg) \ - readw((chip)->remap_addr + ICH6_REG_##reg) -#define azx_writeb(chip,reg,value) \ - writeb(value, (chip)->remap_addr + ICH6_REG_##reg) -#define azx_readb(chip,reg) \ - readb((chip)->remap_addr + ICH6_REG_##reg) - -#define azx_sd_writel(dev,reg,value) \ - writel(value, (dev)->sd_addr + ICH6_REG_##reg) -#define azx_sd_readl(dev,reg) \ - readl((dev)->sd_addr + ICH6_REG_##reg) -#define azx_sd_writew(dev,reg,value) \ - writew(value, (dev)->sd_addr + ICH6_REG_##reg) -#define azx_sd_readw(dev,reg) \ - readw((dev)->sd_addr + ICH6_REG_##reg) -#define azx_sd_writeb(dev,reg,value) \ - writeb(value, (dev)->sd_addr + ICH6_REG_##reg) -#define azx_sd_readb(dev,reg) \ - readb((dev)->sd_addr + ICH6_REG_##reg) - -/* for pcm support */ -#define get_azx_dev(substream) (substream->runtime->private_data) - -#ifdef CONFIG_X86 -static void __mark_pages_wc(struct azx *chip, void *addr, size_t size, bool on) -{ - if (azx_snoop(chip)) - return; - if (addr && size) { - int pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; - if (on) - set_memory_wc((unsigned long)addr, pages); - else - set_memory_wb((unsigned long)addr, pages); - } -} - -static inline void mark_pages_wc(struct azx *chip, struct snd_dma_buffer *buf, - bool on) -{ - __mark_pages_wc(chip, buf->area, buf->bytes, on); -} -static inline void mark_runtime_wc(struct azx *chip, struct azx_dev *azx_dev, - struct snd_pcm_runtime *runtime, bool on) -{ - if (azx_dev->wc_marked != on) { - __mark_pages_wc(chip, runtime->dma_area, runtime->dma_bytes, on); - azx_dev->wc_marked = on; - } -} -#else -/* NOP for other archs */ -static inline void mark_pages_wc(struct azx *chip, struct snd_dma_buffer *buf, - bool on) -{ -} -static inline void mark_runtime_wc(struct azx *chip, struct azx_dev *azx_dev, - struct snd_pcm_runtime *runtime, bool on) -{ -} -#endif - -static int azx_acquire_irq(struct azx *chip, int do_disconnect); -static int azx_send_cmd(struct hda_bus *bus, unsigned int val); -/* - * Interface for HD codec - */ - -/* - * CORB / RIRB interface - */ -static int azx_alloc_cmd_io(struct azx *chip) -{ - int err; - - /* single page (at least 4096 bytes) must suffice for both ringbuffes */ - err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, - snd_dma_pci_data(chip->pci), - PAGE_SIZE, &chip->rb); - if (err < 0) { - snd_printk(KERN_ERR SFX "cannot allocate CORB/RIRB\n"); - return err; - } - mark_pages_wc(chip, &chip->rb, true); - return 0; -} - -static void azx_init_cmd_io(struct azx *chip) -{ - spin_lock_irq(&chip->reg_lock); - /* CORB set up */ - chip->corb.addr = chip->rb.addr; - chip->corb.buf = (u32 *)chip->rb.area; - azx_writel(chip, CORBLBASE, (u32)chip->corb.addr); - azx_writel(chip, CORBUBASE, upper_32_bits(chip->corb.addr)); - - /* set the corb size to 256 entries (ULI requires explicitly) */ - azx_writeb(chip, CORBSIZE, 0x02); - /* set the corb write pointer to 0 */ - azx_writew(chip, CORBWP, 0); - /* reset the corb hw read pointer */ - azx_writew(chip, CORBRP, ICH6_CORBRP_RST); - /* enable corb dma */ - azx_writeb(chip, CORBCTL, ICH6_CORBCTL_RUN); - - /* RIRB set up */ - chip->rirb.addr = chip->rb.addr + 2048; - chip->rirb.buf = (u32 *)(chip->rb.area + 2048); - chip->rirb.wp = chip->rirb.rp = 0; - memset(chip->rirb.cmds, 0, sizeof(chip->rirb.cmds)); - azx_writel(chip, RIRBLBASE, (u32)chip->rirb.addr); - azx_writel(chip, RIRBUBASE, upper_32_bits(chip->rirb.addr)); - - /* set the rirb size to 256 entries (ULI requires explicitly) */ - azx_writeb(chip, RIRBSIZE, 0x02); - /* reset the rirb hw write pointer */ - azx_writew(chip, RIRBWP, ICH6_RIRBWP_RST); - /* set N=1, get RIRB response interrupt for new entry */ - if (chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND) - azx_writew(chip, RINTCNT, 0xc0); - else - azx_writew(chip, RINTCNT, 1); - /* enable rirb dma and response irq */ - azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN | ICH6_RBCTL_IRQ_EN); - spin_unlock_irq(&chip->reg_lock); -} - -static void azx_free_cmd_io(struct azx *chip) -{ - spin_lock_irq(&chip->reg_lock); - /* disable ringbuffer DMAs */ - azx_writeb(chip, RIRBCTL, 0); - azx_writeb(chip, CORBCTL, 0); - spin_unlock_irq(&chip->reg_lock); -} - -static unsigned int azx_command_addr(u32 cmd) -{ - unsigned int addr = cmd >> 28; - - if (addr >= AZX_MAX_CODECS) { - snd_BUG(); - addr = 0; - } - - return addr; -} - -static unsigned int azx_response_addr(u32 res) -{ - unsigned int addr = res & 0xf; - - if (addr >= AZX_MAX_CODECS) { - snd_BUG(); - addr = 0; - } - - return addr; -} - -/* send a command */ -static int azx_corb_send_cmd(struct hda_bus *bus, u32 val) -{ - struct azx *chip = bus->private_data; - unsigned int addr = azx_command_addr(val); - unsigned int wp; - - spin_lock_irq(&chip->reg_lock); - - /* add command to corb */ - wp = azx_readb(chip, CORBWP); - wp++; - wp %= ICH6_MAX_CORB_ENTRIES; - - chip->rirb.cmds[addr]++; - chip->corb.buf[wp] = cpu_to_le32(val); - azx_writel(chip, CORBWP, wp); - - spin_unlock_irq(&chip->reg_lock); - - return 0; -} - -#define ICH6_RIRB_EX_UNSOL_EV (1<<4) - -/* retrieve RIRB entry - called from interrupt handler */ -static void azx_update_rirb(struct azx *chip) -{ - unsigned int rp, wp; - unsigned int addr; - u32 res, res_ex; - - wp = azx_readb(chip, RIRBWP); - if (wp == chip->rirb.wp) - return; - chip->rirb.wp = wp; - - while (chip->rirb.rp != wp) { - chip->rirb.rp++; - chip->rirb.rp %= ICH6_MAX_RIRB_ENTRIES; - - rp = chip->rirb.rp << 1; /* an RIRB entry is 8-bytes */ - res_ex = le32_to_cpu(chip->rirb.buf[rp + 1]); - res = le32_to_cpu(chip->rirb.buf[rp]); - addr = azx_response_addr(res_ex); - if (res_ex & ICH6_RIRB_EX_UNSOL_EV) - snd_hda_queue_unsol_event(chip->bus, res, res_ex); - else if (chip->rirb.cmds[addr]) { - chip->rirb.res[addr] = res; - smp_wmb(); - chip->rirb.cmds[addr]--; - } else - snd_printk(KERN_ERR SFX "spurious response %#x:%#x, " - "last cmd=%#08x\n", - res, res_ex, - chip->last_cmd[addr]); - } -} - -/* receive a response */ -static unsigned int azx_rirb_get_response(struct hda_bus *bus, - unsigned int addr) -{ - struct azx *chip = bus->private_data; - unsigned long timeout; - unsigned long loopcounter; - int do_poll = 0; - - again: - timeout = jiffies + msecs_to_jiffies(1000); - - for (loopcounter = 0;; loopcounter++) { - if (chip->polling_mode || do_poll) { - spin_lock_irq(&chip->reg_lock); - azx_update_rirb(chip); - spin_unlock_irq(&chip->reg_lock); - } - if (!chip->rirb.cmds[addr]) { - smp_rmb(); - bus->rirb_error = 0; - - if (!do_poll) - chip->poll_count = 0; - return chip->rirb.res[addr]; /* the last value */ - } - if (time_after(jiffies, timeout)) - break; - if (bus->needs_damn_long_delay || loopcounter > 3000) - msleep(2); /* temporary workaround */ - else { - udelay(10); - cond_resched(); - } - } - - if (!chip->polling_mode && chip->poll_count < 2) { - snd_printdd(SFX "azx_get_response timeout, " - "polling the codec once: last cmd=0x%08x\n", - chip->last_cmd[addr]); - do_poll = 1; - chip->poll_count++; - goto again; - } - - - if (!chip->polling_mode) { - snd_printk(KERN_WARNING SFX "azx_get_response timeout, " - "switching to polling mode: last cmd=0x%08x\n", - chip->last_cmd[addr]); - chip->polling_mode = 1; - goto again; - } - - if (chip->msi) { - snd_printk(KERN_WARNING SFX "No response from codec, " - "disabling MSI: last cmd=0x%08x\n", - chip->last_cmd[addr]); - free_irq(chip->irq, chip); - chip->irq = -1; - pci_disable_msi(chip->pci); - chip->msi = 0; - if (azx_acquire_irq(chip, 1) < 0) { - bus->rirb_error = 1; - return -1; - } - goto again; - } - - if (chip->probing) { - /* If this critical timeout happens during the codec probing - * phase, this is likely an access to a non-existing codec - * slot. Better to return an error and reset the system. - */ - return -1; - } - - /* a fatal communication error; need either to reset or to fallback - * to the single_cmd mode - */ - bus->rirb_error = 1; - if (bus->allow_bus_reset && !bus->response_reset && !bus->in_reset) { - bus->response_reset = 1; - return -1; /* give a chance to retry */ - } - - snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, " - "switching to single_cmd mode: last cmd=0x%08x\n", - chip->last_cmd[addr]); - chip->single_cmd = 1; - bus->response_reset = 0; - /* release CORB/RIRB */ - azx_free_cmd_io(chip); - /* disable unsolicited responses */ - azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~ICH6_GCTL_UNSOL); - return -1; -} - -/* - * Use the single immediate command instead of CORB/RIRB for simplicity - * - * Note: according to Intel, this is not preferred use. The command was - * intended for the BIOS only, and may get confused with unsolicited - * responses. So, we shouldn't use it for normal operation from the - * driver. - * I left the codes, however, for debugging/testing purposes. - */ - -/* receive a response */ -static int azx_single_wait_for_response(struct azx *chip, unsigned int addr) -{ - int timeout = 50; - - while (timeout--) { - /* check IRV busy bit */ - if (azx_readw(chip, IRS) & ICH6_IRS_VALID) { - /* reuse rirb.res as the response return value */ - chip->rirb.res[addr] = azx_readl(chip, IR); - return 0; - } - udelay(1); - } - if (printk_ratelimit()) - snd_printd(SFX "get_response timeout: IRS=0x%x\n", - azx_readw(chip, IRS)); - chip->rirb.res[addr] = -1; - return -EIO; -} - -/* send a command */ -static int azx_single_send_cmd(struct hda_bus *bus, u32 val) -{ - struct azx *chip = bus->private_data; - unsigned int addr = azx_command_addr(val); - int timeout = 50; - - bus->rirb_error = 0; - while (timeout--) { - /* check ICB busy bit */ - if (!((azx_readw(chip, IRS) & ICH6_IRS_BUSY))) { - /* Clear IRV valid bit */ - azx_writew(chip, IRS, azx_readw(chip, IRS) | - ICH6_IRS_VALID); - azx_writel(chip, IC, val); - azx_writew(chip, IRS, azx_readw(chip, IRS) | - ICH6_IRS_BUSY); - return azx_single_wait_for_response(chip, addr); - } - udelay(1); - } - if (printk_ratelimit()) - snd_printd(SFX "send_cmd timeout: IRS=0x%x, val=0x%x\n", - azx_readw(chip, IRS), val); - return -EIO; -} - -/* receive a response */ -static unsigned int azx_single_get_response(struct hda_bus *bus, - unsigned int addr) -{ - struct azx *chip = bus->private_data; - return chip->rirb.res[addr]; -} - -/* - * The below are the main callbacks from hda_codec. - * - * They are just the skeleton to call sub-callbacks according to the - * current setting of chip->single_cmd. - */ - -/* send a command */ -static int azx_send_cmd(struct hda_bus *bus, unsigned int val) -{ - struct azx *chip = bus->private_data; - - chip->last_cmd[azx_command_addr(val)] = val; - if (chip->single_cmd) - return azx_single_send_cmd(bus, val); - else - return azx_corb_send_cmd(bus, val); -} - -/* get a response */ -static unsigned int azx_get_response(struct hda_bus *bus, - unsigned int addr) -{ - struct azx *chip = bus->private_data; - if (chip->single_cmd) - return azx_single_get_response(bus, addr); - else - return azx_rirb_get_response(bus, addr); -} - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static void azx_power_notify(struct hda_bus *bus); -#endif - -/* reset codec link */ -static int azx_reset(struct azx *chip, int full_reset) -{ - int count; - - if (!full_reset) - goto __skip; - - /* clear STATESTS */ - azx_writeb(chip, STATESTS, STATESTS_INT_MASK); - - /* reset controller */ - azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~ICH6_GCTL_RESET); - - count = 50; - while (azx_readb(chip, GCTL) && --count) - msleep(1); - - /* delay for >= 100us for codec PLL to settle per spec - * Rev 0.9 section 5.5.1 - */ - msleep(1); - - /* Bring controller out of reset */ - azx_writeb(chip, GCTL, azx_readb(chip, GCTL) | ICH6_GCTL_RESET); - - count = 50; - while (!azx_readb(chip, GCTL) && --count) - msleep(1); - - /* Brent Chartrand said to wait >= 540us for codecs to initialize */ - msleep(1); - - __skip: - /* check to see if controller is ready */ - if (!azx_readb(chip, GCTL)) { - snd_printd(SFX "azx_reset: controller not ready!\n"); - return -EBUSY; - } - - /* Accept unsolicited responses */ - if (!chip->single_cmd) - azx_writel(chip, GCTL, azx_readl(chip, GCTL) | - ICH6_GCTL_UNSOL); - - /* detect codecs */ - if (!chip->codec_mask) { - chip->codec_mask = azx_readw(chip, STATESTS); - snd_printdd(SFX "codec_mask = 0x%x\n", chip->codec_mask); - } - - return 0; -} - - -/* - * Lowlevel interface - */ - -/* enable interrupts */ -static void azx_int_enable(struct azx *chip) -{ - /* enable controller CIE and GIE */ - azx_writel(chip, INTCTL, azx_readl(chip, INTCTL) | - ICH6_INT_CTRL_EN | ICH6_INT_GLOBAL_EN); -} - -/* disable interrupts */ -static void azx_int_disable(struct azx *chip) -{ - int i; - - /* disable interrupts in stream descriptor */ - for (i = 0; i < chip->num_streams; i++) { - struct azx_dev *azx_dev = &chip->azx_dev[i]; - azx_sd_writeb(azx_dev, SD_CTL, - azx_sd_readb(azx_dev, SD_CTL) & ~SD_INT_MASK); - } - - /* disable SIE for all streams */ - azx_writeb(chip, INTCTL, 0); - - /* disable controller CIE and GIE */ - azx_writel(chip, INTCTL, azx_readl(chip, INTCTL) & - ~(ICH6_INT_CTRL_EN | ICH6_INT_GLOBAL_EN)); -} - -/* clear interrupts */ -static void azx_int_clear(struct azx *chip) -{ - int i; - - /* clear stream status */ - for (i = 0; i < chip->num_streams; i++) { - struct azx_dev *azx_dev = &chip->azx_dev[i]; - azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK); - } - - /* clear STATESTS */ - azx_writeb(chip, STATESTS, STATESTS_INT_MASK); - - /* clear rirb status */ - azx_writeb(chip, RIRBSTS, RIRB_INT_MASK); - - /* clear int status */ - azx_writel(chip, INTSTS, ICH6_INT_CTRL_EN | ICH6_INT_ALL_STREAM); -} - -/* start a stream */ -static void azx_stream_start(struct azx *chip, struct azx_dev *azx_dev) -{ - /* - * Before stream start, initialize parameter - */ - azx_dev->insufficient = 1; - - /* enable SIE */ - azx_writel(chip, INTCTL, - azx_readl(chip, INTCTL) | (1 << azx_dev->index)); - /* set DMA start and interrupt mask */ - azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) | - SD_CTL_DMA_START | SD_INT_MASK); -} - -/* stop DMA */ -static void azx_stream_clear(struct azx *chip, struct azx_dev *azx_dev) -{ - azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) & - ~(SD_CTL_DMA_START | SD_INT_MASK)); - azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */ -} - -/* stop a stream */ -static void azx_stream_stop(struct azx *chip, struct azx_dev *azx_dev) -{ - azx_stream_clear(chip, azx_dev); - /* disable SIE */ - azx_writel(chip, INTCTL, - azx_readl(chip, INTCTL) & ~(1 << azx_dev->index)); -} - - -/* - * reset and start the controller registers - */ -static void azx_init_chip(struct azx *chip, int full_reset) -{ - if (chip->initialized) - return; - - /* reset controller */ - azx_reset(chip, full_reset); - - /* initialize interrupts */ - azx_int_clear(chip); - azx_int_enable(chip); - - /* initialize the codec command I/O */ - if (!chip->single_cmd) - azx_init_cmd_io(chip); - - /* program the position buffer */ - azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr); - azx_writel(chip, DPUBASE, upper_32_bits(chip->posbuf.addr)); - - chip->initialized = 1; -} - -/* - * initialize the PCI registers - */ -/* update bits in a PCI register byte */ -static void update_pci_byte(struct pci_dev *pci, unsigned int reg, - unsigned char mask, unsigned char val) -{ - unsigned char data; - - pci_read_config_byte(pci, reg, &data); - data &= ~mask; - data |= (val & mask); - pci_write_config_byte(pci, reg, data); -} - -static void azx_init_pci(struct azx *chip) -{ - /* Clear bits 0-2 of PCI register TCSEL (at offset 0x44) - * TCSEL == Traffic Class Select Register, which sets PCI express QOS - * Ensuring these bits are 0 clears playback static on some HD Audio - * codecs. - * The PCI register TCSEL is defined in the Intel manuals. - */ - if (!(chip->driver_caps & AZX_DCAPS_NO_TCSEL)) { - snd_printdd(SFX "Clearing TCSEL\n"); - update_pci_byte(chip->pci, ICH6_PCIREG_TCSEL, 0x07, 0); - } - - /* For ATI SB450/600/700/800/900 and AMD Hudson azalia HD audio, - * we need to enable snoop. - */ - if (chip->driver_caps & AZX_DCAPS_ATI_SNOOP) { - snd_printdd(SFX "Setting ATI snoop: %d\n", azx_snoop(chip)); - update_pci_byte(chip->pci, - ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR, 0x07, - azx_snoop(chip) ? ATI_SB450_HDAUDIO_ENABLE_SNOOP : 0); - } - - /* For NVIDIA HDA, enable snoop */ - if (chip->driver_caps & AZX_DCAPS_NVIDIA_SNOOP) { - snd_printdd(SFX "Setting Nvidia snoop: %d\n", azx_snoop(chip)); - update_pci_byte(chip->pci, - NVIDIA_HDA_TRANSREG_ADDR, - 0x0f, NVIDIA_HDA_ENABLE_COHBITS); - update_pci_byte(chip->pci, - NVIDIA_HDA_ISTRM_COH, - 0x01, NVIDIA_HDA_ENABLE_COHBIT); - update_pci_byte(chip->pci, - NVIDIA_HDA_OSTRM_COH, - 0x01, NVIDIA_HDA_ENABLE_COHBIT); - } - - /* Enable SCH/PCH snoop if needed */ - if (chip->driver_caps & AZX_DCAPS_SCH_SNOOP) { - unsigned short snoop; - pci_read_config_word(chip->pci, INTEL_SCH_HDA_DEVC, &snoop); - if ((!azx_snoop(chip) && !(snoop & INTEL_SCH_HDA_DEVC_NOSNOOP)) || - (azx_snoop(chip) && (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP))) { - snoop &= ~INTEL_SCH_HDA_DEVC_NOSNOOP; - if (!azx_snoop(chip)) - snoop |= INTEL_SCH_HDA_DEVC_NOSNOOP; - pci_write_config_word(chip->pci, INTEL_SCH_HDA_DEVC, snoop); - pci_read_config_word(chip->pci, - INTEL_SCH_HDA_DEVC, &snoop); - } - snd_printdd(SFX "SCH snoop: %s\n", - (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP) - ? "Disabled" : "Enabled"); - } -} - - -static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev); - -/* - * interrupt handler - */ -static irqreturn_t azx_interrupt(int irq, void *dev_id) -{ - struct azx *chip = dev_id; - struct azx_dev *azx_dev; - u32 status; - u8 sd_status; - int i, ok; - - spin_lock(&chip->reg_lock); - - status = azx_readl(chip, INTSTS); - if (status == 0) { - spin_unlock(&chip->reg_lock); - return IRQ_NONE; - } - - for (i = 0; i < chip->num_streams; i++) { - azx_dev = &chip->azx_dev[i]; - if (status & azx_dev->sd_int_sta_mask) { - sd_status = azx_sd_readb(azx_dev, SD_STS); - azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK); - if (!azx_dev->substream || !azx_dev->running || - !(sd_status & SD_INT_COMPLETE)) - continue; - /* check whether this IRQ is really acceptable */ - ok = azx_position_ok(chip, azx_dev); - if (ok == 1) { - azx_dev->irq_pending = 0; - spin_unlock(&chip->reg_lock); - snd_pcm_period_elapsed(azx_dev->substream); - spin_lock(&chip->reg_lock); - } else if (ok == 0 && chip->bus && chip->bus->workq) { - /* bogus IRQ, process it later */ - azx_dev->irq_pending = 1; - queue_work(chip->bus->workq, - &chip->irq_pending_work); - } - } - } - - /* clear rirb int */ - status = azx_readb(chip, RIRBSTS); - if (status & RIRB_INT_MASK) { - if (status & RIRB_INT_RESPONSE) { - if (chip->driver_caps & AZX_DCAPS_RIRB_PRE_DELAY) - udelay(80); - azx_update_rirb(chip); - } - azx_writeb(chip, RIRBSTS, RIRB_INT_MASK); - } - -#if 0 - /* clear state status int */ - if (azx_readb(chip, STATESTS) & 0x04) - azx_writeb(chip, STATESTS, 0x04); -#endif - spin_unlock(&chip->reg_lock); - - return IRQ_HANDLED; -} - - -/* - * set up a BDL entry - */ -static int setup_bdle(struct snd_pcm_substream *substream, - struct azx_dev *azx_dev, u32 **bdlp, - int ofs, int size, int with_ioc) -{ - u32 *bdl = *bdlp; - - while (size > 0) { - dma_addr_t addr; - int chunk; - - if (azx_dev->frags >= AZX_MAX_BDL_ENTRIES) - return -EINVAL; - - addr = snd_pcm_sgbuf_get_addr(substream, ofs); - /* program the address field of the BDL entry */ - bdl[0] = cpu_to_le32((u32)addr); - bdl[1] = cpu_to_le32(upper_32_bits(addr)); - /* program the size field of the BDL entry */ - chunk = snd_pcm_sgbuf_get_chunk_size(substream, ofs, size); - bdl[2] = cpu_to_le32(chunk); - /* program the IOC to enable interrupt - * only when the whole fragment is processed - */ - size -= chunk; - bdl[3] = (size || !with_ioc) ? 0 : cpu_to_le32(0x01); - bdl += 4; - azx_dev->frags++; - ofs += chunk; - } - *bdlp = bdl; - return ofs; -} - -/* - * set up BDL entries - */ -static int azx_setup_periods(struct azx *chip, - struct snd_pcm_substream *substream, - struct azx_dev *azx_dev) -{ - u32 *bdl; - int i, ofs, periods, period_bytes; - int pos_adj; - - /* reset BDL address */ - azx_sd_writel(azx_dev, SD_BDLPL, 0); - azx_sd_writel(azx_dev, SD_BDLPU, 0); - - period_bytes = azx_dev->period_bytes; - periods = azx_dev->bufsize / period_bytes; - - /* program the initial BDL entries */ - bdl = (u32 *)azx_dev->bdl.area; - ofs = 0; - azx_dev->frags = 0; - pos_adj = bdl_pos_adj[chip->dev_index]; - if (pos_adj > 0) { - struct snd_pcm_runtime *runtime = substream->runtime; - int pos_align = pos_adj; - pos_adj = (pos_adj * runtime->rate + 47999) / 48000; - if (!pos_adj) - pos_adj = pos_align; - else - pos_adj = ((pos_adj + pos_align - 1) / pos_align) * - pos_align; - pos_adj = frames_to_bytes(runtime, pos_adj); - if (pos_adj >= period_bytes) { - snd_printk(KERN_WARNING SFX "Too big adjustment %d\n", - bdl_pos_adj[chip->dev_index]); - pos_adj = 0; - } else { - ofs = setup_bdle(substream, azx_dev, - &bdl, ofs, pos_adj, - !substream->runtime->no_period_wakeup); - if (ofs < 0) - goto error; - } - } else - pos_adj = 0; - for (i = 0; i < periods; i++) { - if (i == periods - 1 && pos_adj) - ofs = setup_bdle(substream, azx_dev, &bdl, ofs, - period_bytes - pos_adj, 0); - else - ofs = setup_bdle(substream, azx_dev, &bdl, ofs, - period_bytes, - !substream->runtime->no_period_wakeup); - if (ofs < 0) - goto error; - } - return 0; - - error: - snd_printk(KERN_ERR SFX "Too many BDL entries: buffer=%d, period=%d\n", - azx_dev->bufsize, period_bytes); - return -EINVAL; -} - -/* reset stream */ -static void azx_stream_reset(struct azx *chip, struct azx_dev *azx_dev) -{ - unsigned char val; - int timeout; - - azx_stream_clear(chip, azx_dev); - - azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) | - SD_CTL_STREAM_RESET); - udelay(3); - timeout = 300; - while (!((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) && - --timeout) - ; - val &= ~SD_CTL_STREAM_RESET; - azx_sd_writeb(azx_dev, SD_CTL, val); - udelay(3); - - timeout = 300; - /* waiting for hardware to report that the stream is out of reset */ - while (((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) && - --timeout) - ; - - /* reset first position - may not be synced with hw at this time */ - *azx_dev->posbuf = 0; -} - -/* - * set up the SD for streaming - */ -static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev) -{ - unsigned int val; - /* make sure the run bit is zero for SD */ - azx_stream_clear(chip, azx_dev); - /* program the stream_tag */ - val = azx_sd_readl(azx_dev, SD_CTL); - val = (val & ~SD_CTL_STREAM_TAG_MASK) | - (azx_dev->stream_tag << SD_CTL_STREAM_TAG_SHIFT); - if (!azx_snoop(chip)) - val |= SD_CTL_TRAFFIC_PRIO; - azx_sd_writel(azx_dev, SD_CTL, val); - - /* program the length of samples in cyclic buffer */ - azx_sd_writel(azx_dev, SD_CBL, azx_dev->bufsize); - - /* program the stream format */ - /* this value needs to be the same as the one programmed */ - azx_sd_writew(azx_dev, SD_FORMAT, azx_dev->format_val); - - /* program the stream LVI (last valid index) of the BDL */ - azx_sd_writew(azx_dev, SD_LVI, azx_dev->frags - 1); - - /* program the BDL address */ - /* lower BDL address */ - azx_sd_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl.addr); - /* upper BDL address */ - azx_sd_writel(azx_dev, SD_BDLPU, upper_32_bits(azx_dev->bdl.addr)); - - /* enable the position buffer */ - if (chip->position_fix[0] != POS_FIX_LPIB || - chip->position_fix[1] != POS_FIX_LPIB) { - if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE)) - azx_writel(chip, DPLBASE, - (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE); - } - - /* set the interrupt enable bits in the descriptor control register */ - azx_sd_writel(azx_dev, SD_CTL, - azx_sd_readl(azx_dev, SD_CTL) | SD_INT_MASK); - - 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; - - mutex_lock(&chip->bus->cmd_mutex); - chip->probing = 1; - azx_send_cmd(chip->bus, cmd); - res = azx_get_response(chip->bus, addr); - chip->probing = 0; - mutex_unlock(&chip->bus->cmd_mutex); - if (res == -1) - return -EIO; - snd_printdd(SFX "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); - -static void azx_bus_reset(struct hda_bus *bus) -{ - struct azx *chip = bus->private_data; - - bus->in_reset = 1; - azx_stop_chip(chip); - azx_init_chip(chip, 1); -#ifdef CONFIG_PM - if (chip->initialized) { - struct azx_pcm *p; - list_for_each_entry(p, &chip->pcm_list, list) - snd_pcm_suspend_all(p->pcm); - snd_hda_suspend(chip->bus); - snd_hda_resume(chip->bus); - } -#endif - bus->in_reset = 0; -} - -/* - * Codec initialization - */ - -/* number of codec slots for each chipset: 0 = default slots (i.e. 4) */ -static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] __devinitdata = { - [AZX_DRIVER_NVIDIA] = 8, - [AZX_DRIVER_TERA] = 1, -}; - -static int __devinit azx_codec_create(struct azx *chip, const char *model) -{ - struct hda_bus_template bus_temp; - int c, codecs, err; - int max_slots; - - memset(&bus_temp, 0, sizeof(bus_temp)); - bus_temp.private_data = chip; - bus_temp.modelname = 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; - bus_temp.ops.bus_reset = azx_bus_reset; -#ifdef CONFIG_SND_HDA_POWER_SAVE - bus_temp.power_save = &power_save; - bus_temp.ops.pm_notify = azx_power_notify; -#endif - - err = snd_hda_bus_new(chip->card, &bus_temp, &chip->bus); - if (err < 0) - return err; - - if (chip->driver_caps & AZX_DCAPS_RIRB_DELAY) { - snd_printd(SFX "Enable delay in RIRB handling\n"); - chip->bus->needs_damn_long_delay = 1; - } - - codecs = 0; - max_slots = azx_max_codecs[chip->driver_type]; - if (!max_slots) - max_slots = AZX_DEFAULT_CODECS; - - /* First try to probe all given codec slots */ - for (c = 0; c < max_slots; c++) { - if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) { - if (probe_codec(chip, c) < 0) { - /* Some BIOSen give you wrong codec addresses - * that don't exist - */ - snd_printk(KERN_WARNING SFX - "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 disturbs 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, 1); - } - } - } - - /* AMD chipsets often cause the communication stalls upon certain - * sequence like the pin-detection. It seems that forcing the synced - * access works around the stall. Grrr... - */ - if (chip->driver_caps & AZX_DCAPS_SYNC_WRITE) { - snd_printd(SFX "Enable sync_write for stable communication\n"); - chip->bus->sync_write = 1; - chip->bus->allow_bus_reset = 1; - } - - /* Then create codec instances */ - for (c = 0; c < max_slots; c++) { - if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) { - struct hda_codec *codec; - err = snd_hda_codec_new(chip->bus, c, &codec); - if (err < 0) - continue; - codec->beep_mode = chip->beep_mode; - codecs++; - } - } - if (!codecs) { - snd_printk(KERN_ERR SFX "no codecs initialized\n"); - return -ENXIO; - } - return 0; -} - -/* configure each codec instance */ -static int __devinit azx_codec_configure(struct azx *chip) -{ - struct hda_codec *codec; - list_for_each_entry(codec, &chip->bus->codec_list, list) { - snd_hda_codec_configure(codec); - } - return 0; -} - - -/* - * PCM support - */ - -/* assign a stream for the PCM */ -static inline struct azx_dev * -azx_assign_device(struct azx *chip, struct snd_pcm_substream *substream) -{ - int dev, i, nums; - struct azx_dev *res = NULL; - /* make a non-zero unique key for the substream */ - int key = (substream->pcm->device << 16) | (substream->number << 2) | - (substream->stream + 1); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - dev = chip->playback_index_offset; - nums = chip->playback_streams; - } else { - dev = chip->capture_index_offset; - nums = chip->capture_streams; - } - for (i = 0; i < nums; i++, dev++) - if (!chip->azx_dev[dev].opened) { - res = &chip->azx_dev[dev]; - if (res->assigned_key == key) - break; - } - if (res) { - res->opened = 1; - res->assigned_key = key; - } - return res; -} - -/* release the assigned stream */ -static inline void azx_release_device(struct azx_dev *azx_dev) -{ - azx_dev->opened = 0; -} - -static struct snd_pcm_hardware azx_pcm_hw = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID | - /* No full-resume yet implemented */ - /* SNDRV_PCM_INFO_RESUME |*/ - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_SYNC_START | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP), - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .rates = SNDRV_PCM_RATE_48000, - .rate_min = 48000, - .rate_max = 48000, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = AZX_MAX_BUF_SIZE, - .period_bytes_min = 128, - .period_bytes_max = AZX_MAX_BUF_SIZE / 2, - .periods_min = 2, - .periods_max = AZX_MAX_FRAG, - .fifo_size = 0, -}; - -static int azx_pcm_open(struct snd_pcm_substream *substream) -{ - struct azx_pcm *apcm = snd_pcm_substream_chip(substream); - struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream]; - struct azx *chip = apcm->chip; - struct azx_dev *azx_dev; - struct snd_pcm_runtime *runtime = substream->runtime; - unsigned long flags; - int err; - int buff_step; - - mutex_lock(&chip->open_mutex); - azx_dev = azx_assign_device(chip, substream); - if (azx_dev == NULL) { - mutex_unlock(&chip->open_mutex); - return -EBUSY; - } - runtime->hw = azx_pcm_hw; - runtime->hw.channels_min = hinfo->channels_min; - runtime->hw.channels_max = hinfo->channels_max; - runtime->hw.formats = hinfo->formats; - runtime->hw.rates = hinfo->rates; - snd_pcm_limit_hw_rates(runtime); - snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); - if (chip->align_buffer_size) - /* constrain buffer sizes to be multiple of 128 - bytes. This is more efficient in terms of memory - access but isn't required by the HDA spec and - prevents users from specifying exact period/buffer - sizes. For example for 44.1kHz, a period size set - to 20ms will be rounded to 19.59ms. */ - buff_step = 128; - else - /* Don't enforce steps on buffer sizes, still need to - be multiple of 4 bytes (HDA spec). Tested on Intel - HDA controllers, may not work on all devices where - option needs to be disabled */ - buff_step = 4; - - snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, - buff_step); - snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, - buff_step); - snd_hda_power_up(apcm->codec); - err = hinfo->ops.open(hinfo, apcm->codec, substream); - if (err < 0) { - azx_release_device(azx_dev); - snd_hda_power_down(apcm->codec); - mutex_unlock(&chip->open_mutex); - return err; - } - snd_pcm_limit_hw_rates(runtime); - /* sanity check */ - if (snd_BUG_ON(!runtime->hw.channels_min) || - snd_BUG_ON(!runtime->hw.channels_max) || - snd_BUG_ON(!runtime->hw.formats) || - snd_BUG_ON(!runtime->hw.rates)) { - azx_release_device(azx_dev); - hinfo->ops.close(hinfo, apcm->codec, substream); - snd_hda_power_down(apcm->codec); - mutex_unlock(&chip->open_mutex); - return -EINVAL; - } - spin_lock_irqsave(&chip->reg_lock, flags); - azx_dev->substream = substream; - azx_dev->running = 0; - spin_unlock_irqrestore(&chip->reg_lock, flags); - - runtime->private_data = azx_dev; - snd_pcm_set_sync(substream); - mutex_unlock(&chip->open_mutex); - return 0; -} - -static int azx_pcm_close(struct snd_pcm_substream *substream) -{ - struct azx_pcm *apcm = snd_pcm_substream_chip(substream); - struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream]; - struct azx *chip = apcm->chip; - struct azx_dev *azx_dev = get_azx_dev(substream); - unsigned long flags; - - mutex_lock(&chip->open_mutex); - spin_lock_irqsave(&chip->reg_lock, flags); - azx_dev->substream = NULL; - azx_dev->running = 0; - spin_unlock_irqrestore(&chip->reg_lock, flags); - azx_release_device(azx_dev); - hinfo->ops.close(hinfo, apcm->codec, substream); - snd_hda_power_down(apcm->codec); - mutex_unlock(&chip->open_mutex); - return 0; -} - -static int azx_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct azx_pcm *apcm = snd_pcm_substream_chip(substream); - struct azx *chip = apcm->chip; - struct snd_pcm_runtime *runtime = substream->runtime; - struct azx_dev *azx_dev = get_azx_dev(substream); - int ret; - - mark_runtime_wc(chip, azx_dev, runtime, false); - azx_dev->bufsize = 0; - azx_dev->period_bytes = 0; - azx_dev->format_val = 0; - ret = snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(hw_params)); - if (ret < 0) - return ret; - mark_runtime_wc(chip, azx_dev, runtime, true); - return ret; -} - -static int azx_pcm_hw_free(struct snd_pcm_substream *substream) -{ - struct azx_pcm *apcm = snd_pcm_substream_chip(substream); - struct azx_dev *azx_dev = get_azx_dev(substream); - struct azx *chip = apcm->chip; - struct snd_pcm_runtime *runtime = substream->runtime; - struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream]; - - /* reset BDL address */ - azx_sd_writel(azx_dev, SD_BDLPL, 0); - azx_sd_writel(azx_dev, SD_BDLPU, 0); - azx_sd_writel(azx_dev, SD_CTL, 0); - azx_dev->bufsize = 0; - azx_dev->period_bytes = 0; - azx_dev->format_val = 0; - - snd_hda_codec_cleanup(apcm->codec, hinfo, substream); - - mark_runtime_wc(chip, azx_dev, runtime, false); - return snd_pcm_lib_free_pages(substream); -} - -static int azx_pcm_prepare(struct snd_pcm_substream *substream) -{ - struct azx_pcm *apcm = snd_pcm_substream_chip(substream); - struct azx *chip = apcm->chip; - struct azx_dev *azx_dev = get_azx_dev(substream); - struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream]; - struct snd_pcm_runtime *runtime = substream->runtime; - unsigned int bufsize, period_bytes, format_val, stream_tag; - int err; - struct hda_spdif_out *spdif = - snd_hda_spdif_out_of_nid(apcm->codec, hinfo->nid); - unsigned short ctls = spdif ? spdif->ctls : 0; - - azx_stream_reset(chip, azx_dev); - format_val = snd_hda_calc_stream_format(runtime->rate, - runtime->channels, - runtime->format, - hinfo->maxbps, - ctls); - if (!format_val) { - snd_printk(KERN_ERR SFX - "invalid format_val, rate=%d, ch=%d, format=%d\n", - runtime->rate, runtime->channels, runtime->format); - return -EINVAL; - } - - bufsize = snd_pcm_lib_buffer_bytes(substream); - period_bytes = snd_pcm_lib_period_bytes(substream); - - snd_printdd(SFX "azx_pcm_prepare: bufsize=0x%x, format=0x%x\n", - bufsize, format_val); - - if (bufsize != azx_dev->bufsize || - period_bytes != azx_dev->period_bytes || - format_val != azx_dev->format_val) { - azx_dev->bufsize = bufsize; - azx_dev->period_bytes = period_bytes; - azx_dev->format_val = format_val; - err = azx_setup_periods(chip, substream, azx_dev); - if (err < 0) - return err; - } - - /* wallclk has 24Mhz clock source */ - azx_dev->period_wallclk = (((runtime->period_size * 24000) / - runtime->rate) * 1000); - azx_setup_controller(chip, azx_dev); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - azx_dev->fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE) + 1; - else - azx_dev->fifo_size = 0; - - stream_tag = azx_dev->stream_tag; - /* CA-IBG chips need the playback stream starting from 1 */ - if ((chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND) && - stream_tag > chip->capture_streams) - stream_tag -= chip->capture_streams; - return snd_hda_codec_prepare(apcm->codec, hinfo, stream_tag, - azx_dev->format_val, substream); -} - -static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct azx_pcm *apcm = snd_pcm_substream_chip(substream); - struct azx *chip = apcm->chip; - struct azx_dev *azx_dev; - struct snd_pcm_substream *s; - int rstart = 0, start, nsync = 0, sbits = 0; - int nwait, timeout; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - rstart = 1; - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - case SNDRV_PCM_TRIGGER_RESUME: - start = 1; - break; - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_STOP: - start = 0; - break; - default: - return -EINVAL; - } - - snd_pcm_group_for_each_entry(s, substream) { - if (s->pcm->card != substream->pcm->card) - continue; - azx_dev = get_azx_dev(s); - sbits |= 1 << azx_dev->index; - nsync++; - snd_pcm_trigger_done(s, substream); - } - - spin_lock(&chip->reg_lock); - if (nsync > 1) { - /* first, set SYNC bits of corresponding streams */ - if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC) - azx_writel(chip, OLD_SSYNC, - azx_readl(chip, OLD_SSYNC) | sbits); - else - azx_writel(chip, SSYNC, azx_readl(chip, SSYNC) | sbits); - } - snd_pcm_group_for_each_entry(s, substream) { - if (s->pcm->card != substream->pcm->card) - continue; - azx_dev = get_azx_dev(s); - if (start) { - azx_dev->start_wallclk = azx_readl(chip, WALLCLK); - if (!rstart) - azx_dev->start_wallclk -= - azx_dev->period_wallclk; - azx_stream_start(chip, azx_dev); - } else { - azx_stream_stop(chip, azx_dev); - } - azx_dev->running = start; - } - spin_unlock(&chip->reg_lock); - if (start) { - if (nsync == 1) - return 0; - /* wait until all FIFOs get ready */ - for (timeout = 5000; timeout; timeout--) { - nwait = 0; - snd_pcm_group_for_each_entry(s, substream) { - if (s->pcm->card != substream->pcm->card) - continue; - azx_dev = get_azx_dev(s); - if (!(azx_sd_readb(azx_dev, SD_STS) & - SD_STS_FIFO_READY)) - nwait++; - } - if (!nwait) - break; - cpu_relax(); - } - } else { - /* wait until all RUN bits are cleared */ - for (timeout = 5000; timeout; timeout--) { - nwait = 0; - snd_pcm_group_for_each_entry(s, substream) { - if (s->pcm->card != substream->pcm->card) - continue; - azx_dev = get_azx_dev(s); - if (azx_sd_readb(azx_dev, SD_CTL) & - SD_CTL_DMA_START) - nwait++; - } - if (!nwait) - break; - cpu_relax(); - } - } - if (nsync > 1) { - spin_lock(&chip->reg_lock); - /* reset SYNC bits */ - if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC) - azx_writel(chip, OLD_SSYNC, - azx_readl(chip, OLD_SSYNC) & ~sbits); - else - azx_writel(chip, SSYNC, azx_readl(chip, SSYNC) & ~sbits); - spin_unlock(&chip->reg_lock); - } - return 0; -} - -/* get the current DMA position with correction on VIA chips */ -static unsigned int azx_via_get_position(struct azx *chip, - struct azx_dev *azx_dev) -{ - unsigned int link_pos, mini_pos, bound_pos; - unsigned int mod_link_pos, mod_dma_pos, mod_mini_pos; - unsigned int fifo_size; - - link_pos = azx_sd_readl(azx_dev, SD_LPIB); - if (azx_dev->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - /* Playback, no problem using link position */ - return link_pos; - } - - /* Capture */ - /* For new chipset, - * use mod to get the DMA position just like old chipset - */ - mod_dma_pos = le32_to_cpu(*azx_dev->posbuf); - mod_dma_pos %= azx_dev->period_bytes; - - /* azx_dev->fifo_size can't get FIFO size of in stream. - * Get from base address + offset. - */ - fifo_size = readw(chip->remap_addr + VIA_IN_STREAM0_FIFO_SIZE_OFFSET); - - if (azx_dev->insufficient) { - /* Link position never gather than FIFO size */ - if (link_pos <= fifo_size) - return 0; - - azx_dev->insufficient = 0; - } - - if (link_pos <= fifo_size) - mini_pos = azx_dev->bufsize + link_pos - fifo_size; - else - mini_pos = link_pos - fifo_size; - - /* Find nearest previous boudary */ - mod_mini_pos = mini_pos % azx_dev->period_bytes; - mod_link_pos = link_pos % azx_dev->period_bytes; - if (mod_link_pos >= fifo_size) - bound_pos = link_pos - mod_link_pos; - else if (mod_dma_pos >= mod_mini_pos) - bound_pos = mini_pos - mod_mini_pos; - else { - bound_pos = mini_pos - mod_mini_pos + azx_dev->period_bytes; - if (bound_pos >= azx_dev->bufsize) - bound_pos = 0; - } - - /* Calculate real DMA position we want */ - return bound_pos + mod_dma_pos; -} - -static unsigned int azx_get_position(struct azx *chip, - struct azx_dev *azx_dev, - bool with_check) -{ - unsigned int pos; - int stream = azx_dev->substream->stream; - - switch (chip->position_fix[stream]) { - case POS_FIX_LPIB: - /* read LPIB */ - pos = azx_sd_readl(azx_dev, SD_LPIB); - break; - case POS_FIX_VIACOMBO: - pos = azx_via_get_position(chip, azx_dev); - break; - default: - /* use the position buffer */ - pos = le32_to_cpu(*azx_dev->posbuf); - if (with_check && chip->position_fix[stream] == POS_FIX_AUTO) { - if (!pos || pos == (u32)-1) { - printk(KERN_WARNING - "hda-intel: Invalid position buffer, " - "using LPIB read method instead.\n"); - chip->position_fix[stream] = POS_FIX_LPIB; - pos = azx_sd_readl(azx_dev, SD_LPIB); - } else - chip->position_fix[stream] = POS_FIX_POSBUF; - } - break; - } - - if (pos >= azx_dev->bufsize) - pos = 0; - return pos; -} - -static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream) -{ - struct azx_pcm *apcm = snd_pcm_substream_chip(substream); - struct azx *chip = apcm->chip; - struct azx_dev *azx_dev = get_azx_dev(substream); - return bytes_to_frames(substream->runtime, - azx_get_position(chip, azx_dev, false)); -} - -/* - * Check whether the current DMA position is acceptable for updating - * periods. Returns non-zero if it's OK. - * - * Many HD-audio controllers appear pretty inaccurate about - * the update-IRQ timing. The IRQ is issued before actually the - * data is processed. So, we need to process it afterwords in a - * workqueue. - */ -static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev) -{ - u32 wallclk; - unsigned int pos; - int stream; - - wallclk = azx_readl(chip, WALLCLK) - azx_dev->start_wallclk; - if (wallclk < (azx_dev->period_wallclk * 2) / 3) - return -1; /* bogus (too early) interrupt */ - - stream = azx_dev->substream->stream; - pos = azx_get_position(chip, azx_dev, true); - - if (WARN_ONCE(!azx_dev->period_bytes, - "hda-intel: zero azx_dev->period_bytes")) - return -1; /* this shouldn't happen! */ - if (wallclk < (azx_dev->period_wallclk * 5) / 4 && - pos % azx_dev->period_bytes > azx_dev->period_bytes / 2) - /* NG - it's below the first next period boundary */ - return bdl_pos_adj[chip->dev_index] ? 0 : -1; - azx_dev->start_wallclk += wallclk; - return 1; /* OK, it's fine */ -} - -/* - * The work for pending PCM period updates. - */ -static void azx_irq_pending_work(struct work_struct *work) -{ - struct azx *chip = container_of(work, struct azx, irq_pending_work); - int i, pending, ok; - - if (!chip->irq_pending_warned) { - printk(KERN_WARNING - "hda-intel: IRQ timing workaround is activated " - "for card #%d. Suggest a bigger bdl_pos_adj.\n", - chip->card->number); - chip->irq_pending_warned = 1; - } - - for (;;) { - pending = 0; - spin_lock_irq(&chip->reg_lock); - for (i = 0; i < chip->num_streams; i++) { - struct azx_dev *azx_dev = &chip->azx_dev[i]; - if (!azx_dev->irq_pending || - !azx_dev->substream || - !azx_dev->running) - continue; - ok = azx_position_ok(chip, azx_dev); - if (ok > 0) { - azx_dev->irq_pending = 0; - spin_unlock(&chip->reg_lock); - snd_pcm_period_elapsed(azx_dev->substream); - spin_lock(&chip->reg_lock); - } else if (ok < 0) { - pending = 0; /* too early */ - } else - pending++; - } - spin_unlock_irq(&chip->reg_lock); - if (!pending) - return; - msleep(1); - } -} - -/* clear irq_pending flags and assure no on-going workq */ -static void azx_clear_irq_pending(struct azx *chip) -{ - int i; - - spin_lock_irq(&chip->reg_lock); - for (i = 0; i < chip->num_streams; i++) - chip->azx_dev[i].irq_pending = 0; - spin_unlock_irq(&chip->reg_lock); -} - -#ifdef CONFIG_X86 -static int azx_pcm_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *area) -{ - struct azx_pcm *apcm = snd_pcm_substream_chip(substream); - struct azx *chip = apcm->chip; - if (!azx_snoop(chip)) - area->vm_page_prot = pgprot_writecombine(area->vm_page_prot); - return snd_pcm_lib_default_mmap(substream, area); -} -#else -#define azx_pcm_mmap NULL -#endif - -static struct snd_pcm_ops azx_pcm_ops = { - .open = azx_pcm_open, - .close = azx_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = azx_pcm_hw_params, - .hw_free = azx_pcm_hw_free, - .prepare = azx_pcm_prepare, - .trigger = azx_pcm_trigger, - .pointer = azx_pcm_pointer, - .mmap = azx_pcm_mmap, - .page = snd_pcm_sgbuf_ops_page, -}; - -static void azx_pcm_free(struct snd_pcm *pcm) -{ - struct azx_pcm *apcm = pcm->private_data; - if (apcm) { - list_del(&apcm->list); - kfree(apcm); - } -} - -#define MAX_PREALLOC_SIZE (32 * 1024 * 1024) - -static int -azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, - struct hda_pcm *cpcm) -{ - struct azx *chip = bus->private_data; - struct snd_pcm *pcm; - struct azx_pcm *apcm; - int pcm_dev = cpcm->device; - unsigned int size; - int s, err; - - list_for_each_entry(apcm, &chip->pcm_list, list) { - if (apcm->pcm->device == 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; - strlcpy(pcm->name, cpcm->name, sizeof(pcm->name)); - apcm = kzalloc(sizeof(*apcm), GFP_KERNEL); - if (apcm == NULL) - return -ENOMEM; - apcm->chip = chip; - apcm->pcm = pcm; - apcm->codec = codec; - pcm->private_data = apcm; - pcm->private_free = azx_pcm_free; - if (cpcm->pcm_type == HDA_PCM_TYPE_MODEM) - pcm->dev_class = SNDRV_PCM_CLASS_MODEM; - list_add_tail(&apcm->list, &chip->pcm_list); - 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 */ - size = CONFIG_SND_HDA_PREALLOC_SIZE * 1024; - if (size > MAX_PREALLOC_SIZE) - size = MAX_PREALLOC_SIZE; - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, - snd_dma_pci_data(chip->pci), - size, MAX_PREALLOC_SIZE); - return 0; -} - -/* - * mixer creation - all stuff is implemented in hda module - */ -static int __devinit azx_mixer_create(struct azx *chip) -{ - return snd_hda_build_controls(chip->bus); -} - - -/* - * initialize SD streams - */ -static int __devinit azx_init_stream(struct azx *chip) -{ - int i; - - /* initialize each stream (aka device) - * assign the starting bdl address to each stream (device) - * and initialize - */ - for (i = 0; i < chip->num_streams; i++) { - struct azx_dev *azx_dev = &chip->azx_dev[i]; - azx_dev->posbuf = (u32 __iomem *)(chip->posbuf.area + i * 8); - /* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */ - azx_dev->sd_addr = chip->remap_addr + (0x20 * i + 0x80); - /* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */ - azx_dev->sd_int_sta_mask = 1 << i; - /* stream tag: must be non-zero and unique */ - azx_dev->index = i; - azx_dev->stream_tag = i + 1; - } - - return 0; -} - -static int azx_acquire_irq(struct azx *chip, int do_disconnect) -{ - if (request_irq(chip->pci->irq, azx_interrupt, - chip->msi ? 0 : IRQF_SHARED, - KBUILD_MODNAME, chip)) { - printk(KERN_ERR "hda-intel: unable to grab IRQ %d, " - "disabling device\n", chip->pci->irq); - if (do_disconnect) - snd_card_disconnect(chip->card); - return -1; - } - chip->irq = chip->pci->irq; - pci_intx(chip->pci, !chip->msi); - return 0; -} - - -static void azx_stop_chip(struct azx *chip) -{ - if (!chip->initialized) - return; - - /* disable interrupts */ - azx_int_disable(chip); - azx_int_clear(chip); - - /* disable CORB/RIRB */ - azx_free_cmd_io(chip); - - /* disable position buffer */ - azx_writel(chip, DPLBASE, 0); - azx_writel(chip, DPUBASE, 0); - - chip->initialized = 0; -} - -#ifdef CONFIG_SND_HDA_POWER_SAVE -/* power-up/down the controller */ -static void azx_power_notify(struct hda_bus *bus) -{ - struct azx *chip = bus->private_data; - struct hda_codec *c; - int power_on = 0; - - list_for_each_entry(c, &bus->codec_list, list) { - if (c->power_on) { - power_on = 1; - break; - } - } - if (power_on) - azx_init_chip(chip, 1); - else if (chip->running && power_save_controller && - !bus->power_keep_link_on) - azx_stop_chip(chip); -} -#endif /* CONFIG_SND_HDA_POWER_SAVE */ - -#ifdef CONFIG_PM -/* - * 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); - struct azx *chip = card->private_data; - struct azx_pcm *p; - - snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - azx_clear_irq_pending(chip); - list_for_each_entry(p, &chip->pcm_list, list) - snd_pcm_suspend_all(p->pcm); - if (chip->initialized) - snd_hda_suspend(chip->bus); - azx_stop_chip(chip); - if (chip->irq >= 0) { - free_irq(chip->irq, chip); - chip->irq = -1; - } - if (chip->msi) - pci_disable_msi(chip->pci); - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); - return 0; -} - -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"); - snd_card_disconnect(card); - return -EIO; - } - pci_set_master(pci); - if (chip->msi) - if (pci_enable_msi(pci) < 0) - chip->msi = 0; - if (azx_acquire_irq(chip, 1) < 0) - return -EIO; - azx_init_pci(chip); - - if (snd_hda_codecs_inuse(chip->bus)) - azx_init_chip(chip, 1); - - snd_hda_resume(chip->bus); - snd_power_change_state(card, SNDRV_CTL_POWER_D0); - return 0; -} -#endif /* CONFIG_PM */ - - -/* - * 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); - snd_hda_bus_reboot_notify(chip->bus); - 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++) - azx_stream_stop(chip, &chip->azx_dev[i]); - azx_stop_chip(chip); - } - - if (chip->irq >= 0) - free_irq(chip->irq, (void*)chip); - if (chip->msi) - pci_disable_msi(chip->pci); - if (chip->remap_addr) - iounmap(chip->remap_addr); - - if (chip->azx_dev) { - for (i = 0; i < chip->num_streams; i++) - if (chip->azx_dev[i].bdl.area) { - mark_pages_wc(chip, &chip->azx_dev[i].bdl, false); - snd_dma_free_pages(&chip->azx_dev[i].bdl); - } - } - if (chip->rb.area) { - mark_pages_wc(chip, &chip->rb, false); - snd_dma_free_pages(&chip->rb); - } - if (chip->posbuf.area) { - mark_pages_wc(chip, &chip->posbuf, false); - snd_dma_free_pages(&chip->posbuf); - } - pci_release_regions(chip->pci); - pci_disable_device(chip->pci); - kfree(chip->azx_dev); - kfree(chip); - - return 0; -} - -static int azx_dev_free(struct snd_device *device) -{ - return azx_free(device->device_data); -} - -/* - * white/black-listing for position_fix - */ -static struct snd_pci_quirk position_fix_list[] __devinitdata = { - SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_LPIB), - SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_LPIB), - SND_PCI_QUIRK(0x103c, 0x306d, "HP dv3", POS_FIX_LPIB), - SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB), - SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS", POS_FIX_LPIB), - SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS M2V", POS_FIX_LPIB), - SND_PCI_QUIRK(0x104d, 0x9069, "Sony VPCS11V9E", POS_FIX_LPIB), - SND_PCI_QUIRK(0x10de, 0xcb89, "Macbook Pro 7,1", POS_FIX_LPIB), - SND_PCI_QUIRK(0x1297, 0x3166, "Shuttle", POS_FIX_LPIB), - SND_PCI_QUIRK(0x1458, 0xa022, "ga-ma770-ud3", POS_FIX_LPIB), - SND_PCI_QUIRK(0x1462, 0x1002, "MSI Wind U115", POS_FIX_LPIB), - SND_PCI_QUIRK(0x1565, 0x8218, "Biostar Microtech", POS_FIX_LPIB), - SND_PCI_QUIRK(0x1849, 0x0888, "775Dual-VSTA", POS_FIX_LPIB), - SND_PCI_QUIRK(0x8086, 0x2503, "DG965OT AAD63733-203", POS_FIX_LPIB), - {} -}; - -static int __devinit check_position_fix(struct azx *chip, int fix) -{ - const struct snd_pci_quirk *q; - - switch (fix) { - case POS_FIX_LPIB: - case POS_FIX_POSBUF: - case POS_FIX_VIACOMBO: - case POS_FIX_COMBO: - return fix; - } - - q = snd_pci_quirk_lookup(chip->pci, position_fix_list); - if (q) { - printk(KERN_INFO - "hda_intel: position_fix set to %d " - "for device %04x:%04x\n", - q->value, q->subvendor, q->subdevice); - return q->value; - } - - /* Check VIA/ATI HD Audio Controller exist */ - if (chip->driver_caps & AZX_DCAPS_POSFIX_VIA) { - snd_printd(SFX "Using VIACOMBO position fix\n"); - return POS_FIX_VIACOMBO; - } - if (chip->driver_caps & AZX_DCAPS_POSFIX_LPIB) { - snd_printd(SFX "Using LPIB position fix\n"); - return POS_FIX_LPIB; - } - return POS_FIX_AUTO; -} - -/* - * black-lists for probe_mask - */ -static struct snd_pci_quirk probe_mask_list[] __devinitdata = { - /* Thinkpad often breaks the controller communication when accessing - * to the non-working (or non-existing) modem codec slot. - */ - 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), - /* forced codec slots */ - SND_PCI_QUIRK(0x1043, 0x1262, "ASUS W5Fm", 0x103), - SND_PCI_QUIRK(0x1046, 0x1262, "ASUS W5F", 0x103), - {} -}; - -#define AZX_FORCE_CODEC_MASK 0x100 - -static void __devinit check_probe_mask(struct azx *chip, int dev) -{ - const struct snd_pci_quirk *q; - - chip->codec_probe_mask = probe_mask[dev]; - if (chip->codec_probe_mask == -1) { - q = snd_pci_quirk_lookup(chip->pci, probe_mask_list); - if (q) { - printk(KERN_INFO - "hda_intel: probe_mask set to 0x%x " - "for device %04x:%04x\n", - q->value, q->subvendor, q->subdevice); - chip->codec_probe_mask = q->value; - } - } - - /* check forced option */ - if (chip->codec_probe_mask != -1 && - (chip->codec_probe_mask & AZX_FORCE_CODEC_MASK)) { - chip->codec_mask = chip->codec_probe_mask & 0xff; - printk(KERN_INFO "hda_intel: codec_mask forced to 0x%x\n", - chip->codec_mask); - } -} - -/* - * white/black-list for enable_msi - */ -static struct snd_pci_quirk msi_black_list[] __devinitdata = { - SND_PCI_QUIRK(0x1043, 0x81f2, "ASUS", 0), /* Athlon64 X2 + nvidia */ - SND_PCI_QUIRK(0x1043, 0x81f6, "ASUS", 0), /* nvidia */ - SND_PCI_QUIRK(0x1043, 0x822d, "ASUS", 0), /* Athlon64 X2 + nvidia MCP55 */ - SND_PCI_QUIRK(0x1849, 0x0888, "ASRock", 0), /* Athlon64 X2 + nvidia */ - SND_PCI_QUIRK(0xa0a0, 0x0575, "Aopen MZ915-M", 0), /* ICH6 */ - {} -}; - -static void __devinit check_msi(struct azx *chip) -{ - const struct snd_pci_quirk *q; - - if (enable_msi >= 0) { - chip->msi = !!enable_msi; - return; - } - chip->msi = 1; /* enable MSI as default */ - q = snd_pci_quirk_lookup(chip->pci, msi_black_list); - if (q) { - printk(KERN_INFO - "hda_intel: msi for device %04x:%04x set to %d\n", - q->subvendor, q->subdevice, q->value); - chip->msi = q->value; - return; - } - - /* NVidia chipsets seem to cause troubles with MSI */ - if (chip->driver_caps & AZX_DCAPS_NO_MSI) { - printk(KERN_INFO "hda_intel: Disabling MSI\n"); - chip->msi = 0; - } -} - -/* check the snoop mode availability */ -static void __devinit azx_check_snoop_available(struct azx *chip) -{ - bool snoop = chip->snoop; - - switch (chip->driver_type) { - case AZX_DRIVER_VIA: - /* force to non-snoop mode for a new VIA controller - * when BIOS is set - */ - if (snoop) { - u8 val; - pci_read_config_byte(chip->pci, 0x42, &val); - if (!(val & 0x80) && chip->pci->revision == 0x30) - snoop = false; - } - break; - case AZX_DRIVER_ATIHDMI_NS: - /* new ATI HDMI requires non-snoop */ - snoop = false; - break; - } - - if (snoop != chip->snoop) { - snd_printk(KERN_INFO SFX "Force to %s mode\n", - snoop ? "snoop" : "non-snoop"); - chip->snoop = snoop; - } -} - -/* - * constructor - */ -static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, - int dev, unsigned int driver_caps, - struct azx **rchip) -{ - struct azx *chip; - int i, err; - unsigned short gcap; - static struct snd_device_ops ops = { - .dev_free = azx_dev_free, - }; - - *rchip = NULL; - - err = pci_enable_device(pci); - if (err < 0) - return err; - - chip = kzalloc(sizeof(*chip), GFP_KERNEL); - if (!chip) { - snd_printk(KERN_ERR SFX "cannot allocate chip\n"); - pci_disable_device(pci); - return -ENOMEM; - } - - spin_lock_init(&chip->reg_lock); - mutex_init(&chip->open_mutex); - chip->card = card; - chip->pci = pci; - chip->irq = -1; - chip->driver_caps = driver_caps; - chip->driver_type = driver_caps & 0xff; - check_msi(chip); - chip->dev_index = dev; - INIT_WORK(&chip->irq_pending_work, azx_irq_pending_work); - INIT_LIST_HEAD(&chip->pcm_list); - - chip->position_fix[0] = chip->position_fix[1] = - check_position_fix(chip, position_fix[dev]); - /* combo mode uses LPIB for playback */ - if (chip->position_fix[0] == POS_FIX_COMBO) { - chip->position_fix[0] = POS_FIX_LPIB; - chip->position_fix[1] = POS_FIX_AUTO; - } - - check_probe_mask(chip, dev); - - chip->single_cmd = single_cmd; - chip->snoop = hda_snoop; - azx_check_snoop_available(chip); - - if (bdl_pos_adj[dev] < 0) { - switch (chip->driver_type) { - case AZX_DRIVER_ICH: - case AZX_DRIVER_PCH: - bdl_pos_adj[dev] = 1; - break; - default: - bdl_pos_adj[dev] = 32; - break; - } - } - -#if BITS_PER_LONG != 64 - /* Fix up base address on ULI M5461 */ - if (chip->driver_type == AZX_DRIVER_ULI) { - u16 tmp3; - pci_read_config_word(pci, 0x40, &tmp3); - pci_write_config_word(pci, 0x40, tmp3 | 0x10); - pci_write_config_dword(pci, PCI_BASE_ADDRESS_1, 0); - } -#endif - - err = pci_request_regions(pci, "ICH HD audio"); - if (err < 0) { - kfree(chip); - pci_disable_device(pci); - return err; - } - - chip->addr = pci_resource_start(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; - goto errout; - } - - if (chip->msi) - if (pci_enable_msi(pci) < 0) - chip->msi = 0; - - if (azx_acquire_irq(chip, 0) < 0) { - err = -EBUSY; - goto errout; - } - - pci_set_master(pci); - synchronize_irq(chip->irq); - - gcap = azx_readw(chip, GCAP); - snd_printdd(SFX "chipset global capabilities = 0x%x\n", gcap); - - /* disable SB600 64bit support for safety */ - if (chip->pci->vendor == PCI_VENDOR_ID_ATI) { - struct pci_dev *p_smbus; - p_smbus = pci_get_device(PCI_VENDOR_ID_ATI, - PCI_DEVICE_ID_ATI_SBX00_SMBUS, - NULL); - if (p_smbus) { - if (p_smbus->revision < 0x30) - gcap &= ~ICH6_GCAP_64OK; - pci_dev_put(p_smbus); - } - } - - /* disable 64bit DMA address on some devices */ - if (chip->driver_caps & AZX_DCAPS_NO_64BIT) { - snd_printd(SFX "Disabling 64bit DMA\n"); - gcap &= ~ICH6_GCAP_64OK; - } - - /* disable buffer size rounding to 128-byte multiples if supported */ - if (align_buffer_size >= 0) - chip->align_buffer_size = !!align_buffer_size; - else { - if (chip->driver_caps & AZX_DCAPS_BUFSIZE) - chip->align_buffer_size = 0; - else if (chip->driver_caps & AZX_DCAPS_ALIGN_BUFSIZE) - chip->align_buffer_size = 1; - else - chip->align_buffer_size = 1; - } - - /* allow 64bit DMA address if supported by H/W */ - if ((gcap & ICH6_GCAP_64OK) && !pci_set_dma_mask(pci, DMA_BIT_MASK(64))) - pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(64)); - else { - pci_set_dma_mask(pci, DMA_BIT_MASK(32)); - pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)); - } - - /* read number of streams from GCAP register instead of using - * hardcoded value - */ - chip->capture_streams = (gcap >> 8) & 0x0f; - chip->playback_streams = (gcap >> 12) & 0x0f; - if (!chip->playback_streams && !chip->capture_streams) { - /* gcap didn't give any info, switching to old method */ - - switch (chip->driver_type) { - case AZX_DRIVER_ULI: - chip->playback_streams = ULI_NUM_PLAYBACK; - chip->capture_streams = ULI_NUM_CAPTURE; - break; - case AZX_DRIVER_ATIHDMI: - case AZX_DRIVER_ATIHDMI_NS: - 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; - break; - } - } - chip->capture_index_offset = 0; - chip->playback_index_offset = chip->capture_streams; - chip->num_streams = chip->playback_streams + chip->capture_streams; - chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev), - GFP_KERNEL); - if (!chip->azx_dev) { - snd_printk(KERN_ERR SFX "cannot malloc azx_dev\n"); - goto errout; - } - - for (i = 0; i < chip->num_streams; i++) { - /* allocate memory for the BDL for each stream */ - err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, - snd_dma_pci_data(chip->pci), - BDL_SIZE, &chip->azx_dev[i].bdl); - if (err < 0) { - snd_printk(KERN_ERR SFX "cannot allocate BDL\n"); - goto errout; - } - mark_pages_wc(chip, &chip->azx_dev[i].bdl, true); - } - /* allocate memory for the position buffer */ - err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, - snd_dma_pci_data(chip->pci), - chip->num_streams * 8, &chip->posbuf); - if (err < 0) { - snd_printk(KERN_ERR SFX "cannot allocate posbuf\n"); - goto errout; - } - mark_pages_wc(chip, &chip->posbuf, true); - /* allocate CORB/RIRB */ - err = azx_alloc_cmd_io(chip); - if (err < 0) - goto errout; - - /* initialize streams */ - azx_init_stream(chip); - - /* initialize chip */ - azx_init_pci(chip); - azx_init_chip(chip, (probe_only[dev] & 2) == 0); - - /* codec detection */ - if (!chip->codec_mask) { - snd_printk(KERN_ERR SFX "no codecs found!\n"); - err = -ENODEV; - goto errout; - } - - err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); - if (err <0) { - snd_printk(KERN_ERR SFX "Error creating device [card]!\n"); - goto errout; - } - - strcpy(card->driver, "HDA-Intel"); - strlcpy(card->shortname, driver_short_names[chip->driver_type], - sizeof(card->shortname)); - snprintf(card->longname, sizeof(card->longname), - "%s at 0x%lx irq %i", - card->shortname, chip->addr, chip->irq); - - *rchip = chip; - return 0; - - errout: - azx_free(chip); - return err; -} - -static void power_down_all_codecs(struct azx *chip) -{ -#ifdef CONFIG_SND_HDA_POWER_SAVE - /* The codecs were powered up in snd_hda_codec_new(). - * Now all initialization done, so turn them down if possible - */ - struct hda_codec *codec; - list_for_each_entry(codec, &chip->bus->codec_list, list) { - snd_hda_power_down(codec); - } -#endif -} - -static int __devinit azx_probe(struct pci_dev *pci, - const struct pci_device_id *pci_id) -{ - static int dev; - struct snd_card *card; - struct azx *chip; - int err; - - if (dev >= SNDRV_CARDS) - return -ENODEV; - if (!enable[dev]) { - dev++; - return -ENOENT; - } - - err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card); - if (err < 0) { - snd_printk(KERN_ERR SFX "Error creating card!\n"); - return err; - } - - /* set this here since it's referred in snd_hda_load_patch() */ - snd_card_set_dev(card, &pci->dev); - - err = azx_create(card, pci, dev, pci_id->driver_data, &chip); - if (err < 0) - goto out_free; - card->private_data = chip; - -#ifdef CONFIG_SND_HDA_INPUT_BEEP - chip->beep_mode = beep_mode[dev]; -#endif - - /* create codec instances */ - err = azx_codec_create(chip, model[dev]); - if (err < 0) - goto out_free; -#ifdef CONFIG_SND_HDA_PATCH_LOADER - if (patch[dev] && *patch[dev]) { - snd_printk(KERN_ERR SFX "Applying patch firmware '%s'\n", - patch[dev]); - err = snd_hda_load_patch(chip->bus, patch[dev]); - if (err < 0) - goto out_free; - } -#endif - if ((probe_only[dev] & 1) == 0) { - err = azx_codec_configure(chip); - if (err < 0) - goto out_free; - } - - /* create PCM streams */ - err = snd_hda_build_pcms(chip->bus); - if (err < 0) - goto out_free; - - /* create mixer controls */ - err = azx_mixer_create(chip); - if (err < 0) - goto out_free; - - err = snd_card_register(card); - 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) -{ - snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); -} - -/* PCI IDs */ -static DEFINE_PCI_DEVICE_TABLE(azx_ids) = { - /* CPT */ - { PCI_DEVICE(0x8086, 0x1c20), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP | - AZX_DCAPS_BUFSIZE }, - /* PBG */ - { PCI_DEVICE(0x8086, 0x1d20), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP | - AZX_DCAPS_BUFSIZE}, - /* Panther Point */ - { PCI_DEVICE(0x8086, 0x1e20), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP | - AZX_DCAPS_BUFSIZE}, - /* Lynx Point */ - { PCI_DEVICE(0x8086, 0x8c20), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP | - AZX_DCAPS_BUFSIZE}, - /* SCH */ - { PCI_DEVICE(0x8086, 0x811b), - .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_SCH_SNOOP | - AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_LPIB }, /* Poulsbo */ - { PCI_DEVICE(0x8086, 0x080a), - .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_SCH_SNOOP | - AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_LPIB }, /* Oaktrail */ - /* ICH */ - { PCI_DEVICE(0x8086, 0x2668), - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC | - AZX_DCAPS_BUFSIZE }, /* ICH6 */ - { PCI_DEVICE(0x8086, 0x27d8), - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC | - AZX_DCAPS_BUFSIZE }, /* ICH7 */ - { PCI_DEVICE(0x8086, 0x269a), - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC | - AZX_DCAPS_BUFSIZE }, /* ESB2 */ - { PCI_DEVICE(0x8086, 0x284b), - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC | - AZX_DCAPS_BUFSIZE }, /* ICH8 */ - { PCI_DEVICE(0x8086, 0x293e), - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC | - AZX_DCAPS_BUFSIZE }, /* ICH9 */ - { PCI_DEVICE(0x8086, 0x293f), - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC | - AZX_DCAPS_BUFSIZE }, /* ICH9 */ - { PCI_DEVICE(0x8086, 0x3a3e), - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC | - AZX_DCAPS_BUFSIZE }, /* ICH10 */ - { PCI_DEVICE(0x8086, 0x3a6e), - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC | - AZX_DCAPS_BUFSIZE }, /* ICH10 */ - /* Generic Intel */ - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_ANY_ID), - .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, - .class_mask = 0xffffff, - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_BUFSIZE }, - /* ATI SB 450/600/700/800/900 */ - { PCI_DEVICE(0x1002, 0x437b), - .driver_data = AZX_DRIVER_ATI | AZX_DCAPS_PRESET_ATI_SB }, - { PCI_DEVICE(0x1002, 0x4383), - .driver_data = AZX_DRIVER_ATI | AZX_DCAPS_PRESET_ATI_SB }, - /* AMD Hudson */ - { PCI_DEVICE(0x1022, 0x780d), - .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_SB }, - /* ATI HDMI */ - { PCI_DEVICE(0x1002, 0x793b), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0x7919), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0x960f), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0x970f), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0xaa00), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0xaa08), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0xaa10), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0xaa18), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0xaa20), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0xaa28), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0xaa30), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0xaa38), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0xaa40), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0xaa48), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0x9902), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0xaaa0), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0xaaa8), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0xaab0), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI }, - /* VIA VT8251/VT8237A */ - { PCI_DEVICE(0x1106, 0x3288), - .driver_data = AZX_DRIVER_VIA | AZX_DCAPS_POSFIX_VIA }, - /* SIS966 */ - { PCI_DEVICE(0x1039, 0x7502), .driver_data = AZX_DRIVER_SIS }, - /* ULI M5461 */ - { PCI_DEVICE(0x10b9, 0x5461), .driver_data = AZX_DRIVER_ULI }, - /* NVIDIA MCP */ - { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID), - .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, - .class_mask = 0xffffff, - .driver_data = AZX_DRIVER_NVIDIA | AZX_DCAPS_PRESET_NVIDIA }, - /* Teradici */ - { PCI_DEVICE(0x6549, 0x1200), - .driver_data = AZX_DRIVER_TERA | AZX_DCAPS_NO_64BIT }, - /* Creative X-Fi (CA0110-IBG) */ -#if !defined(CONFIG_SND_CTXFI) && !defined(CONFIG_SND_CTXFI_MODULE) - /* the following entry conflicts with snd-ctxfi driver, - * as ctxfi driver mutates from HD-audio to native mode with - * a special command sequence. - */ - { PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_ANY_ID), - .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, - .class_mask = 0xffffff, - .driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND | - AZX_DCAPS_RIRB_PRE_DELAY | AZX_DCAPS_POSFIX_LPIB }, -#else - /* this entry seems still valid -- i.e. without emu20kx chip */ - { PCI_DEVICE(0x1102, 0x0009), - .driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND | - AZX_DCAPS_RIRB_PRE_DELAY | AZX_DCAPS_POSFIX_LPIB }, -#endif - /* Vortex86MX */ - { PCI_DEVICE(0x17f3, 0x3010), .driver_data = AZX_DRIVER_GENERIC }, - /* VMware HDAudio */ - { PCI_DEVICE(0x15ad, 0x1977), .driver_data = AZX_DRIVER_GENERIC }, - /* AMD/ATI 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 | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_ANY_ID), - .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, - .class_mask = 0xffffff, - .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_HDMI }, - { 0, } -}; -MODULE_DEVICE_TABLE(pci, azx_ids); - -/* pci_driver definition */ -static struct pci_driver driver = { - .name = KBUILD_MODNAME, - .id_table = azx_ids, - .probe = azx_probe, - .remove = __devexit_p(azx_remove), -#ifdef CONFIG_PM - .suspend = azx_suspend, - .resume = azx_resume, -#endif -}; - -static int __init alsa_card_azx_init(void) -{ - return pci_register_driver(&driver); -} - -static void __exit alsa_card_azx_exit(void) -{ - pci_unregister_driver(&driver); -} - -module_init(alsa_card_azx_init) -module_exit(alsa_card_azx_exit) diff --git a/ANDROID_3.4.5/sound/pci/hda/hda_jack.c b/ANDROID_3.4.5/sound/pci/hda/hda_jack.c deleted file mode 100644 index d6894849..00000000 --- a/ANDROID_3.4.5/sound/pci/hda/hda_jack.c +++ /dev/null @@ -1,375 +0,0 @@ -/* - * Jack-detection handling for HD-audio - * - * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de> - * - * 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. - */ - -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/export.h> -#include <sound/core.h> -#include <sound/control.h> -#include <sound/jack.h> -#include "hda_codec.h" -#include "hda_local.h" -#include "hda_jack.h" - -bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid) -{ - if (codec->no_jack_detect) - return false; - if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_PRES_DETECT)) - return false; - if (!codec->ignore_misc_bit && - (get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) & - AC_DEFCFG_MISC_NO_PRESENCE)) - return false; - if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP)) - return false; - return true; -} -EXPORT_SYMBOL_HDA(is_jack_detectable); - -/* execute pin sense measurement */ -static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid) -{ - u32 pincap; - - if (!codec->no_trigger_sense) { - pincap = snd_hda_query_pin_caps(codec, nid); - if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */ - snd_hda_codec_read(codec, nid, 0, - AC_VERB_SET_PIN_SENSE, 0); - } - return snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_SENSE, 0); -} - -/** - * snd_hda_jack_tbl_get - query the jack-table entry for the given NID - */ -struct hda_jack_tbl * -snd_hda_jack_tbl_get(struct hda_codec *codec, hda_nid_t nid) -{ - struct hda_jack_tbl *jack = codec->jacktbl.list; - int i; - - if (!nid || !jack) - return NULL; - for (i = 0; i < codec->jacktbl.used; i++, jack++) - if (jack->nid == nid) - return jack; - return NULL; -} -EXPORT_SYMBOL_HDA(snd_hda_jack_tbl_get); - -/** - * snd_hda_jack_tbl_get_from_tag - query the jack-table entry for the given tag - */ -struct hda_jack_tbl * -snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec, unsigned char tag) -{ - struct hda_jack_tbl *jack = codec->jacktbl.list; - int i; - - if (!tag || !jack) - return NULL; - for (i = 0; i < codec->jacktbl.used; i++, jack++) - if (jack->tag == tag) - return jack; - return NULL; -} -EXPORT_SYMBOL_HDA(snd_hda_jack_tbl_get_from_tag); - -/** - * snd_hda_jack_tbl_new - create a jack-table entry for the given NID - */ -struct hda_jack_tbl * -snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid) -{ - struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid); - if (jack) - return jack; - snd_array_init(&codec->jacktbl, sizeof(*jack), 16); - jack = snd_array_new(&codec->jacktbl); - if (!jack) - return NULL; - jack->nid = nid; - jack->jack_dirty = 1; - jack->tag = codec->jacktbl.used; - return jack; -} -EXPORT_SYMBOL_HDA(snd_hda_jack_tbl_new); - -void snd_hda_jack_tbl_clear(struct hda_codec *codec) -{ -#ifdef CONFIG_SND_HDA_INPUT_JACK - /* free jack instances manually when clearing/reconfiguring */ - if (!codec->bus->shutdown && codec->jacktbl.list) { - struct hda_jack_tbl *jack = codec->jacktbl.list; - int i; - for (i = 0; i < codec->jacktbl.used; i++, jack++) { - if (jack->jack) - snd_device_free(codec->bus->card, jack->jack); - } - } -#endif - snd_array_free(&codec->jacktbl); -} - -/* update the cached value and notification flag if needed */ -static void jack_detect_update(struct hda_codec *codec, - struct hda_jack_tbl *jack) -{ - if (jack->jack_dirty || !jack->jack_detect) { - jack->pin_sense = read_pin_sense(codec, jack->nid); - jack->jack_dirty = 0; - } -} - -/** - * snd_hda_set_dirty_all - Mark all the cached as dirty - * - * This function sets the dirty flag to all entries of jack table. - * It's called from the resume path in hda_codec.c. - */ -void snd_hda_jack_set_dirty_all(struct hda_codec *codec) -{ - struct hda_jack_tbl *jack = codec->jacktbl.list; - int i; - - for (i = 0; i < codec->jacktbl.used; i++, jack++) - if (jack->nid) - jack->jack_dirty = 1; -} -EXPORT_SYMBOL_HDA(snd_hda_jack_set_dirty_all); - -/** - * snd_hda_pin_sense - execute pin sense measurement - * @codec: the CODEC to sense - * @nid: the pin NID to sense - * - * Execute necessary pin sense measurement and return its Presence Detect, - * Impedance, ELD Valid etc. status bits. - */ -u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid) -{ - struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid); - if (jack) { - jack_detect_update(codec, jack); - return jack->pin_sense; - } - return read_pin_sense(codec, nid); -} -EXPORT_SYMBOL_HDA(snd_hda_pin_sense); - -#define get_jack_plug_state(sense) !!(sense & AC_PINSENSE_PRESENCE) - -/** - * snd_hda_jack_detect - query pin Presence Detect status - * @codec: the CODEC to sense - * @nid: the pin NID to sense - * - * Query and return the pin's Presence Detect status. - */ -int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid) -{ - u32 sense = snd_hda_pin_sense(codec, nid); - return get_jack_plug_state(sense); -} -EXPORT_SYMBOL_HDA(snd_hda_jack_detect); - -/** - * snd_hda_jack_detect_enable - enable the jack-detection - */ -int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid, - unsigned char action) -{ - struct hda_jack_tbl *jack = snd_hda_jack_tbl_new(codec, nid); - if (!jack) - return -ENOMEM; - if (jack->jack_detect) - return 0; /* already registered */ - jack->jack_detect = 1; - if (action) - jack->action = action; - return snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | jack->tag); -} -EXPORT_SYMBOL_HDA(snd_hda_jack_detect_enable); - -/** - * snd_hda_jack_report_sync - sync the states of all jacks and report if changed - */ -void snd_hda_jack_report_sync(struct hda_codec *codec) -{ - struct hda_jack_tbl *jack = codec->jacktbl.list; - int i, state; - - for (i = 0; i < codec->jacktbl.used; i++, jack++) - if (jack->nid) { - jack_detect_update(codec, jack); - if (!jack->kctl) - continue; - state = get_jack_plug_state(jack->pin_sense); - snd_kctl_jack_report(codec->bus->card, jack->kctl, state); -#ifdef CONFIG_SND_HDA_INPUT_JACK - if (jack->jack) - snd_jack_report(jack->jack, - state ? jack->type : 0); -#endif - } -} -EXPORT_SYMBOL_HDA(snd_hda_jack_report_sync); - -#ifdef CONFIG_SND_HDA_INPUT_JACK -/* guess the jack type from the pin-config */ -static int get_input_jack_type(struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); - switch (get_defcfg_device(def_conf)) { - case AC_JACK_LINE_OUT: - case AC_JACK_SPEAKER: - return SND_JACK_LINEOUT; - case AC_JACK_HP_OUT: - return SND_JACK_HEADPHONE; - case AC_JACK_SPDIF_OUT: - case AC_JACK_DIG_OTHER_OUT: - return SND_JACK_AVOUT; - case AC_JACK_MIC_IN: - return SND_JACK_MICROPHONE; - default: - return SND_JACK_LINEIN; - } -} - -static void hda_free_jack_priv(struct snd_jack *jack) -{ - struct hda_jack_tbl *jacks = jack->private_data; - jacks->nid = 0; - jacks->jack = NULL; -} -#endif - -/** - * snd_hda_jack_add_kctl - Add a kctl for the given pin - * - * This assigns a jack-detection kctl to the given pin. The kcontrol - * will have the given name and index. - */ -int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, - const char *name, int idx) -{ - struct hda_jack_tbl *jack; - struct snd_kcontrol *kctl; - int err, state; - - jack = snd_hda_jack_tbl_new(codec, nid); - if (!jack) - return 0; - if (jack->kctl) - return 0; /* already created */ - kctl = snd_kctl_jack_new(name, idx, codec); - if (!kctl) - return -ENOMEM; - err = snd_hda_ctl_add(codec, nid, kctl); - if (err < 0) - return err; - jack->kctl = kctl; - state = snd_hda_jack_detect(codec, nid); - snd_kctl_jack_report(codec->bus->card, kctl, state); -#ifdef CONFIG_SND_HDA_INPUT_JACK - jack->type = get_input_jack_type(codec, nid); - err = snd_jack_new(codec->bus->card, name, jack->type, &jack->jack); - if (err < 0) - return err; - jack->jack->private_data = jack; - jack->jack->private_free = hda_free_jack_priv; - snd_jack_report(jack->jack, state ? jack->type : 0); -#endif - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_jack_add_kctl); - -static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid, - const struct auto_pin_cfg *cfg, - char *lastname, int *lastidx) -{ - unsigned int def_conf, conn; - char name[44]; - int idx, err; - - if (!nid) - return 0; - if (!is_jack_detectable(codec, nid)) - return 0; - def_conf = snd_hda_codec_get_pincfg(codec, nid); - conn = get_defcfg_connect(def_conf); - if (conn != AC_JACK_PORT_COMPLEX) - return 0; - - snd_hda_get_pin_label(codec, nid, cfg, name, sizeof(name), &idx); - if (!strcmp(name, lastname) && idx == *lastidx) - idx++; - strncpy(lastname, name, 44); - *lastidx = idx; - err = snd_hda_jack_add_kctl(codec, nid, name, idx); - if (err < 0) - return err; - return snd_hda_jack_detect_enable(codec, nid, 0); -} - -/** - * snd_hda_jack_add_kctls - Add kctls for all pins included in the given pincfg - */ -int snd_hda_jack_add_kctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - const hda_nid_t *p; - int i, err, lastidx = 0; - char lastname[44] = ""; - - for (i = 0, p = cfg->line_out_pins; i < cfg->line_outs; i++, p++) { - err = add_jack_kctl(codec, *p, cfg, lastname, &lastidx); - if (err < 0) - return err; - } - for (i = 0, p = cfg->hp_pins; i < cfg->hp_outs; i++, p++) { - if (*p == *cfg->line_out_pins) /* might be duplicated */ - break; - err = add_jack_kctl(codec, *p, cfg, lastname, &lastidx); - if (err < 0) - return err; - } - for (i = 0, p = cfg->speaker_pins; i < cfg->speaker_outs; i++, p++) { - if (*p == *cfg->line_out_pins) /* might be duplicated */ - break; - err = add_jack_kctl(codec, *p, cfg, lastname, &lastidx); - if (err < 0) - return err; - } - for (i = 0; i < cfg->num_inputs; i++) { - err = add_jack_kctl(codec, cfg->inputs[i].pin, cfg, lastname, &lastidx); - if (err < 0) - return err; - } - for (i = 0, p = cfg->dig_out_pins; i < cfg->dig_outs; i++, p++) { - err = add_jack_kctl(codec, *p, cfg, lastname, &lastidx); - if (err < 0) - return err; - } - err = add_jack_kctl(codec, cfg->dig_in_pin, cfg, lastname, &lastidx); - if (err < 0) - return err; - err = add_jack_kctl(codec, cfg->mono_out_pin, cfg, lastname, &lastidx); - if (err < 0) - return err; - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_jack_add_kctls); diff --git a/ANDROID_3.4.5/sound/pci/hda/hda_jack.h b/ANDROID_3.4.5/sound/pci/hda/hda_jack.h deleted file mode 100644 index c66655cf..00000000 --- a/ANDROID_3.4.5/sound/pci/hda/hda_jack.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Jack-detection handling for HD-audio - * - * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de> - * - * 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. - */ - -#ifndef __SOUND_HDA_JACK_H -#define __SOUND_HDA_JACK_H - -struct hda_jack_tbl { - hda_nid_t nid; - unsigned char action; /* event action (0 = none) */ - unsigned char tag; /* unsol event tag */ - unsigned int private_data; /* arbitrary data */ - /* jack-detection stuff */ - unsigned int pin_sense; /* cached pin-sense value */ - unsigned int jack_detect:1; /* capable of jack-detection? */ - unsigned int jack_dirty:1; /* needs to update? */ - struct snd_kcontrol *kctl; /* assigned kctl for jack-detection */ -#ifdef CONFIG_SND_HDA_INPUT_JACK - int type; - struct snd_jack *jack; -#endif -}; - -struct hda_jack_tbl * -snd_hda_jack_tbl_get(struct hda_codec *codec, hda_nid_t nid); -struct hda_jack_tbl * -snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec, unsigned char tag); - -struct hda_jack_tbl * -snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid); -void snd_hda_jack_tbl_clear(struct hda_codec *codec); - -/** - * snd_hda_jack_get_action - get jack-tbl entry for the tag - * - * Call this from the unsol event handler to get the assigned action for the - * event. This will mark the dirty flag for the later reporting, too. - */ -static inline unsigned char -snd_hda_jack_get_action(struct hda_codec *codec, unsigned int tag) -{ - struct hda_jack_tbl *jack = snd_hda_jack_tbl_get_from_tag(codec, tag); - if (jack) { - jack->jack_dirty = 1; - return jack->action; - } - return 0; -} - -void snd_hda_jack_set_dirty_all(struct hda_codec *codec); - -int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid, - unsigned char action); - -u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid); -int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid); - -bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid); - -int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, - const char *name, int idx); -int snd_hda_jack_add_kctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg); - -void snd_hda_jack_report_sync(struct hda_codec *codec); - - -#endif /* __SOUND_HDA_JACK_H */ diff --git a/ANDROID_3.4.5/sound/pci/hda/hda_local.h b/ANDROID_3.4.5/sound/pci/hda/hda_local.h deleted file mode 100644 index 0ec92481..00000000 --- a/ANDROID_3.4.5/sound/pci/hda/hda_local.h +++ /dev/null @@ -1,708 +0,0 @@ -/* - * Universal Interface for Intel High Definition Audio Codec - * - * Local helper functions - * - * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> - * - * 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. - */ - -#ifndef __SOUND_HDA_LOCAL_H -#define __SOUND_HDA_LOCAL_H - -/* We abuse kcontrol_new.subdev field to pass the NID corresponding to - * the given new control. If id.subdev has a bit flag HDA_SUBDEV_NID_FLAG, - * snd_hda_ctl_add() takes the lower-bit subdev value as a valid NID. - * - * Note that the subdevice field is cleared again before the real registration - * in snd_hda_ctl_add(), so that this value won't appear in the outside. - */ -#define HDA_SUBDEV_NID_FLAG (1U << 31) -#define HDA_SUBDEV_AMP_FLAG (1U << 30) - -/* - * for mixer controls - */ -#define HDA_COMPOSE_AMP_VAL_OFS(nid,chs,idx,dir,ofs) \ - ((nid) | ((chs)<<16) | ((dir)<<18) | ((idx)<<19) | ((ofs)<<23)) -#define HDA_AMP_VAL_MIN_MUTE (1<<29) -#define HDA_COMPOSE_AMP_VAL(nid,chs,idx,dir) \ - HDA_COMPOSE_AMP_VAL_OFS(nid, chs, idx, dir, 0) -/* mono volume with index (index=0,1,...) (channel=1,2) */ -#define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, dir, flags) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ - .subdevice = HDA_SUBDEV_AMP_FLAG, \ - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ - SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ - SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \ - .info = snd_hda_mixer_amp_volume_info, \ - .get = snd_hda_mixer_amp_volume_get, \ - .put = snd_hda_mixer_amp_volume_put, \ - .tlv = { .c = snd_hda_mixer_amp_tlv }, \ - .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, dir) | flags } -/* stereo volume with index */ -#define HDA_CODEC_VOLUME_IDX(xname, xcidx, nid, xindex, direction) \ - HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, 3, xindex, direction, 0) -/* mono volume */ -#define HDA_CODEC_VOLUME_MONO(xname, nid, channel, xindex, direction) \ - HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, channel, xindex, direction, 0) -/* stereo volume */ -#define HDA_CODEC_VOLUME(xname, nid, xindex, direction) \ - HDA_CODEC_VOLUME_MONO(xname, nid, 3, xindex, direction) -/* stereo volume with min=mute */ -#define HDA_CODEC_VOLUME_MIN_MUTE(xname, nid, xindex, direction) \ - HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, 3, xindex, direction, \ - HDA_AMP_VAL_MIN_MUTE) -/* mono mute switch with index (index=0,1,...) (channel=1,2) */ -#define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ - .subdevice = HDA_SUBDEV_AMP_FLAG, \ - .info = snd_hda_mixer_amp_switch_info, \ - .get = snd_hda_mixer_amp_switch_get, \ - .put = snd_hda_mixer_amp_switch_put, \ - .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) } -/* stereo mute switch with index */ -#define HDA_CODEC_MUTE_IDX(xname, xcidx, nid, xindex, direction) \ - HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, 3, xindex, direction) -/* mono mute switch */ -#define HDA_CODEC_MUTE_MONO(xname, nid, channel, xindex, direction) \ - HDA_CODEC_MUTE_MONO_IDX(xname, 0, nid, channel, xindex, direction) -/* stereo mute switch */ -#define HDA_CODEC_MUTE(xname, nid, xindex, direction) \ - HDA_CODEC_MUTE_MONO(xname, nid, 3, xindex, direction) -#ifdef CONFIG_SND_HDA_INPUT_BEEP -/* special beep mono mute switch with index (index=0,1,...) (channel=1,2) */ -#define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ - .subdevice = HDA_SUBDEV_AMP_FLAG, \ - .info = snd_hda_mixer_amp_switch_info, \ - .get = snd_hda_mixer_amp_switch_get, \ - .put = snd_hda_mixer_amp_switch_put_beep, \ - .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) } -#else -/* no digital beep - just the standard one */ -#define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, ch, xidx, dir) \ - HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, ch, xidx, dir) -#endif /* CONFIG_SND_HDA_INPUT_BEEP */ -/* special beep mono mute switch */ -#define HDA_CODEC_MUTE_BEEP_MONO(xname, nid, channel, xindex, direction) \ - HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, 0, nid, channel, xindex, direction) -/* special beep stereo mute switch */ -#define HDA_CODEC_MUTE_BEEP(xname, nid, xindex, direction) \ - HDA_CODEC_MUTE_BEEP_MONO(xname, nid, 3, xindex, direction) - -extern const char *snd_hda_pcm_type_name[]; - -int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo); -int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, - unsigned int size, unsigned int __user *tlv); -int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo); -int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -#ifdef CONFIG_SND_HDA_INPUT_BEEP -int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -#endif -/* lowlevel accessor with caching; use carefully */ -int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, - int direction, int index); -int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, - int direction, int idx, int mask, int val); -int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, - int dir, int idx, int mask, int val); -#ifdef CONFIG_PM -void snd_hda_codec_resume_amp(struct hda_codec *codec); -#endif - -void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir, - unsigned int *tlv); -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 * const *slaves, - const char *suffix, bool init_slave_vol, - struct snd_kcontrol **ctl_ret); -#define snd_hda_add_vmaster(codec, name, tlv, slaves, suffix) \ - __snd_hda_add_vmaster(codec, name, tlv, slaves, suffix, true, NULL) -int snd_hda_codec_reset(struct hda_codec *codec); - -enum { - HDA_VMUTE_OFF, - HDA_VMUTE_ON, - HDA_VMUTE_FOLLOW_MASTER, -}; - -struct hda_vmaster_mute_hook { - /* below two fields must be filled by the caller of - * snd_hda_add_vmaster_hook() beforehand - */ - struct snd_kcontrol *sw_kctl; - void (*hook)(void *, int); - /* below are initialized automatically */ - unsigned int mute_mode; /* HDA_VMUTE_XXX */ - struct hda_codec *codec; -}; - -int snd_hda_add_vmaster_hook(struct hda_codec *codec, - struct hda_vmaster_mute_hook *hook, - bool expose_enum_ctl); -void snd_hda_sync_vmaster_hook(struct hda_vmaster_mute_hook *hook); - -/* amp value bits */ -#define HDA_AMP_MUTE 0x80 -#define HDA_AMP_UNMUTE 0x00 -#define HDA_AMP_VOLMASK 0x7f - -/* mono switch binding multiple inputs */ -#define HDA_BIND_MUTE_MONO(xname, nid, channel, indices, direction) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ - .info = snd_hda_mixer_amp_switch_info, \ - .get = snd_hda_mixer_bind_switch_get, \ - .put = snd_hda_mixer_bind_switch_put, \ - .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, indices, direction) } - -/* stereo switch binding multiple inputs */ -#define HDA_BIND_MUTE(xname,nid,indices,dir) \ - HDA_BIND_MUTE_MONO(xname,nid,3,indices,dir) - -int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); - -/* more generic bound controls */ -struct hda_ctl_ops { - snd_kcontrol_info_t *info; - snd_kcontrol_get_t *get; - snd_kcontrol_put_t *put; - snd_kcontrol_tlv_rw_t *tlv; -}; - -extern struct hda_ctl_ops snd_hda_bind_vol; /* for bind-volume with TLV */ -extern struct hda_ctl_ops snd_hda_bind_sw; /* for bind-switch */ - -struct hda_bind_ctls { - struct hda_ctl_ops *ops; - unsigned long values[]; -}; - -int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo); -int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag, - unsigned int size, unsigned int __user *tlv); - -#define HDA_BIND_VOL(xname, bindrec) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\ - SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ - SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,\ - .info = snd_hda_mixer_bind_ctls_info,\ - .get = snd_hda_mixer_bind_ctls_get,\ - .put = snd_hda_mixer_bind_ctls_put,\ - .tlv = { .c = snd_hda_mixer_bind_tlv },\ - .private_value = (long) (bindrec) } -#define HDA_BIND_SW(xname, bindrec) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER,\ - .name = xname, \ - .info = snd_hda_mixer_bind_ctls_info,\ - .get = snd_hda_mixer_bind_ctls_get,\ - .put = snd_hda_mixer_bind_ctls_put,\ - .private_value = (long) (bindrec) } - -/* - * SPDIF I/O - */ -int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, - hda_nid_t associated_nid, - hda_nid_t cvt_nid); -int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid); - -/* - * input MUX helper - */ -#define HDA_MAX_NUM_INPUTS 16 -struct hda_input_mux_item { - char label[32]; - unsigned int index; -}; -struct hda_input_mux { - unsigned int num_items; - struct hda_input_mux_item items[HDA_MAX_NUM_INPUTS]; -}; - -int snd_hda_input_mux_info(const struct hda_input_mux *imux, - struct snd_ctl_elem_info *uinfo); -int snd_hda_input_mux_put(struct hda_codec *codec, - const struct hda_input_mux *imux, - struct snd_ctl_elem_value *ucontrol, hda_nid_t nid, - unsigned int *cur_val); - -/* - * Channel mode helper - */ -struct hda_channel_mode { - int channels; - const struct hda_verb *sequence; -}; - -int snd_hda_ch_mode_info(struct hda_codec *codec, - struct snd_ctl_elem_info *uinfo, - const struct hda_channel_mode *chmode, - int num_chmodes); -int snd_hda_ch_mode_get(struct hda_codec *codec, - struct snd_ctl_elem_value *ucontrol, - const struct hda_channel_mode *chmode, - int num_chmodes, - int max_channels); -int snd_hda_ch_mode_put(struct hda_codec *codec, - struct snd_ctl_elem_value *ucontrol, - const struct hda_channel_mode *chmode, - int num_chmodes, - int *max_channelsp); - -/* - * Multi-channel / digital-out PCM helper - */ - -enum { HDA_FRONT, HDA_REAR, HDA_CLFE, HDA_SIDE }; /* index for dac_nidx */ -enum { HDA_DIG_NONE, HDA_DIG_EXCLUSIVE, HDA_DIG_ANALOG_DUP }; /* dig_out_used */ - -#define HDA_MAX_OUTS 5 - -struct hda_multi_out { - int num_dacs; /* # of DACs, must be more than 1 */ - const hda_nid_t *dac_nids; /* DAC list */ - hda_nid_t hp_nid; /* optional DAC for HP, 0 when not exists */ - hda_nid_t hp_out_nid[HDA_MAX_OUTS]; /* DACs for multiple HPs */ - hda_nid_t extra_out_nid[HDA_MAX_OUTS]; /* other (e.g. speaker) DACs */ - hda_nid_t dig_out_nid; /* digital out audio widget */ - const hda_nid_t *slave_dig_outs; - int max_channels; /* currently supported analog channels */ - int dig_out_used; /* current usage of digital out (HDA_DIG_XXX) */ - int no_share_stream; /* don't share a stream with multiple pins */ - int share_spdif; /* share SPDIF pin */ - /* PCM information for both analog and SPDIF DACs */ - unsigned int analog_rates; - unsigned int analog_maxbps; - u64 analog_formats; - unsigned int spdif_rates; - unsigned int spdif_maxbps; - u64 spdif_formats; -}; - -int snd_hda_create_spdif_share_sw(struct hda_codec *codec, - struct hda_multi_out *mout); -int snd_hda_multi_out_dig_open(struct hda_codec *codec, - struct hda_multi_out *mout); -int snd_hda_multi_out_dig_close(struct hda_codec *codec, - struct hda_multi_out *mout); -int snd_hda_multi_out_dig_prepare(struct hda_codec *codec, - struct hda_multi_out *mout, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream); -int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec, - struct hda_multi_out *mout); -int snd_hda_multi_out_analog_open(struct hda_codec *codec, - struct hda_multi_out *mout, - struct snd_pcm_substream *substream, - struct hda_pcm_stream *hinfo); -int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, - struct hda_multi_out *mout, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream); -int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, - struct hda_multi_out *mout); - -/* - * generic codec parser - */ -#ifdef CONFIG_SND_HDA_GENERIC -int snd_hda_parse_generic_codec(struct hda_codec *codec); -#else -static inline int snd_hda_parse_generic_codec(struct hda_codec *codec) -{ - return -ENODEV; -} -#endif - -/* - * generic proc interface - */ -#ifdef CONFIG_PROC_FS -int snd_hda_codec_proc_new(struct hda_codec *codec); -#else -static inline int snd_hda_codec_proc_new(struct hda_codec *codec) { return 0; } -#endif - -#define SND_PRINT_BITS_ADVISED_BUFSIZE 16 -void snd_print_pcm_bits(int pcm, char *buf, int buflen); - -/* - * Misc - */ -int snd_hda_check_board_config(struct hda_codec *codec, int num_configs, - const char * const *modelnames, - const struct snd_pci_quirk *pci_list); -int snd_hda_check_board_codec_sid_config(struct hda_codec *codec, - int num_configs, const char * const *models, - const struct snd_pci_quirk *tbl); -int snd_hda_add_new_ctls(struct hda_codec *codec, - const struct snd_kcontrol_new *knew); - -/* - * unsolicited event handler - */ - -#define HDA_UNSOL_QUEUE_SIZE 64 - -struct hda_bus_unsolicited { - /* ring buffer */ - u32 queue[HDA_UNSOL_QUEUE_SIZE * 2]; - unsigned int rp, wp; - - /* workqueue */ - struct work_struct work; - struct hda_bus *bus; -}; - -/* - * Helper for automatic pin configuration - */ - -enum { - AUTO_PIN_MIC, - AUTO_PIN_LINE_IN, - AUTO_PIN_CD, - AUTO_PIN_AUX, - AUTO_PIN_LAST -}; - -enum { - AUTO_PIN_LINE_OUT, - AUTO_PIN_SPEAKER_OUT, - AUTO_PIN_HP_OUT -}; - -#define AUTO_CFG_MAX_OUTS HDA_MAX_OUTS -#define AUTO_CFG_MAX_INS 8 - -struct auto_pin_cfg_item { - hda_nid_t pin; - int type; -}; - -struct auto_pin_cfg; -const char *hda_get_autocfg_input_label(struct hda_codec *codec, - const struct auto_pin_cfg *cfg, - int input); -int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, - const struct auto_pin_cfg *cfg, - char *label, int maxlen, int *indexp); -int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label, - int index, int *type_index_ret); - -enum { - INPUT_PIN_ATTR_UNUSED, /* pin not connected */ - INPUT_PIN_ATTR_INT, /* internal mic/line-in */ - INPUT_PIN_ATTR_DOCK, /* docking mic/line-in */ - INPUT_PIN_ATTR_NORMAL, /* mic/line-in jack */ - INPUT_PIN_ATTR_FRONT, /* mic/line-in jack in front */ - INPUT_PIN_ATTR_REAR, /* mic/line-in jack in rear */ -}; - -int snd_hda_get_input_pin_attr(unsigned int def_conf); - -struct auto_pin_cfg { - int line_outs; - /* sorted in the order of Front/Surr/CLFE/Side */ - hda_nid_t line_out_pins[AUTO_CFG_MAX_OUTS]; - int speaker_outs; - hda_nid_t speaker_pins[AUTO_CFG_MAX_OUTS]; - int hp_outs; - int line_out_type; /* AUTO_PIN_XXX_OUT */ - hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS]; - int num_inputs; - struct auto_pin_cfg_item inputs[AUTO_CFG_MAX_INS]; - int dig_outs; - hda_nid_t dig_out_pins[2]; - hda_nid_t dig_in_pin; - hda_nid_t mono_out_pin; - int dig_out_type[2]; /* HDA_PCM_TYPE_XXX */ - int dig_in_type; /* HDA_PCM_TYPE_XXX */ -}; - -#define get_defcfg_connect(cfg) \ - ((cfg & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT) -#define get_defcfg_association(cfg) \ - ((cfg & AC_DEFCFG_DEF_ASSOC) >> AC_DEFCFG_ASSOC_SHIFT) -#define get_defcfg_location(cfg) \ - ((cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT) -#define get_defcfg_sequence(cfg) \ - (cfg & AC_DEFCFG_SEQUENCE) -#define get_defcfg_device(cfg) \ - ((cfg & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT) -#define get_defcfg_misc(cfg) \ - ((cfg & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT) - -/* bit-flags for snd_hda_parse_pin_def_config() behavior */ -#define HDA_PINCFG_NO_HP_FIXUP (1 << 0) /* no HP-split */ -#define HDA_PINCFG_NO_LO_FIXUP (1 << 1) /* don't take other outs as LO */ - -int snd_hda_parse_pin_defcfg(struct hda_codec *codec, - struct auto_pin_cfg *cfg, - const hda_nid_t *ignore_nids, - unsigned int cond_flags); - -/* older function */ -#define snd_hda_parse_pin_def_config(codec, cfg, ignore) \ - snd_hda_parse_pin_defcfg(codec, cfg, ignore, 0) - -/* 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 -/* pinctl values */ -#define PIN_IN (AC_PINCTL_IN_EN) -#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_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_HP (AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN) -#define PIN_HP_AMP (AC_PINCTL_HP_EN) - -/* - * get widget capabilities - */ -static inline u32 get_wcaps(struct hda_codec *codec, hda_nid_t nid) -{ - if (nid < codec->start_nid || - nid >= codec->start_nid + codec->num_nodes) - return 0; - return codec->wcaps[nid - codec->start_nid]; -} - -/* get the widget type from widget capability bits */ -static inline int get_wcaps_type(unsigned int wcaps) -{ - if (!wcaps) - return -1; /* invalid type */ - return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; -} - -static inline unsigned int get_wcaps_channels(u32 wcaps) -{ - unsigned int chans; - - chans = (wcaps & AC_WCAP_CHAN_CNT_EXT) >> 13; - chans = ((chans << 1) | 1) + 1; - - return chans; -} - -u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction); -int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, - unsigned int caps); -u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid); -int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid, - unsigned int caps); - -/* flags for hda_nid_item */ -#define HDA_NID_ITEM_AMP (1<<0) - -struct hda_nid_item { - struct snd_kcontrol *kctl; - unsigned int index; - hda_nid_t nid; - unsigned short flags; -}; - -int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, - struct snd_kcontrol *kctl); -int snd_hda_add_nid(struct hda_codec *codec, struct snd_kcontrol *kctl, - unsigned int index, hda_nid_t nid); -void snd_hda_ctls_clear(struct hda_codec *codec); - -/* - * 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 - -#if defined(CONFIG_SND_HDA_POWER_SAVE) && defined(CONFIG_SND_HDA_HWDEP) -int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec); -#else -static inline int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec) -{ - 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 - -#ifdef CONFIG_SND_HDA_RECONFIG -const char *snd_hda_get_hint(struct hda_codec *codec, const char *key); -int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key); -#else -static inline -const char *snd_hda_get_hint(struct hda_codec *codec, const char *key) -{ - return NULL; -} - -static inline -int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key) -{ - return -ENOENT; -} -#endif - -/* - * power-management - */ - -void snd_hda_schedule_power_save(struct hda_codec *codec); - -struct hda_amp_list { - hda_nid_t nid; - unsigned char dir; - unsigned char idx; -}; - -struct hda_loopback_check { - const struct hda_amp_list *amplist; - int power_on; -}; - -int snd_hda_check_amp_list_power(struct hda_codec *codec, - struct hda_loopback_check *check, - hda_nid_t nid); - -/* - * AMP control callbacks - */ -/* retrieve parameters from private_value */ -#define get_amp_nid_(pv) ((pv) & 0xffff) -#define get_amp_nid(kc) get_amp_nid_((kc)->private_value) -#define get_amp_channels(kc) (((kc)->private_value >> 16) & 0x3) -#define get_amp_direction_(pv) (((pv) >> 18) & 0x1) -#define get_amp_direction(kc) get_amp_direction_((kc)->private_value) -#define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf) -#define get_amp_offset(kc) (((kc)->private_value >> 23) & 0x3f) -#define get_amp_min_mute(kc) (((kc)->private_value >> 29) & 0x1) - -/* - * 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_SIZE 256 -#define ELD_MAX_MNL 16 -#define ELD_MAX_SAD 16 - -/* - * ELD: EDID Like Data - */ -struct hdmi_eld { - bool monitor_present; - bool eld_valid; - int eld_size; - int baseline_len; - int eld_ver; - 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]; - /* - * all fields above eld_buffer will be cleared before updating ELD - */ - char eld_buffer[ELD_MAX_SIZE]; -#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); -void snd_hdmi_eld_update_pcm_info(struct hdmi_eld *eld, - struct hda_pcm_stream *hinfo); - -#ifdef CONFIG_PROC_FS -int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld, - int index); -void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld); -#else -static inline int snd_hda_eld_proc_new(struct hda_codec *codec, - struct hdmi_eld *eld, - int index) -{ - 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/ANDROID_3.4.5/sound/pci/hda/hda_proc.c b/ANDROID_3.4.5/sound/pci/hda/hda_proc.c deleted file mode 100644 index e59e2f05..00000000 --- a/ANDROID_3.4.5/sound/pci/hda/hda_proc.c +++ /dev/null @@ -1,742 +0,0 @@ -/* - * Universal Interface for Intel High Definition Audio Codec - * - * Generic proc interface - * - * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> - * - * - * 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 "hda_codec.h" -#include "hda_local.h" - -static char *bits_names(unsigned int bits, char *names[], int size) -{ - int i, n; - static char buf[128]; - - for (i = 0, n = 0; i < size; i++) { - if (bits & (1U<<i) && names[i]) - n += snprintf(buf + n, sizeof(buf) - n, " %s", - names[i]); - } - buf[n] = '\0'; - - return buf; -} - -static const char *get_wid_type_name(unsigned int wid_value) -{ - static char *names[16] = { - [AC_WID_AUD_OUT] = "Audio Output", - [AC_WID_AUD_IN] = "Audio Input", - [AC_WID_AUD_MIX] = "Audio Mixer", - [AC_WID_AUD_SEL] = "Audio Selector", - [AC_WID_PIN] = "Pin Complex", - [AC_WID_POWER] = "Power Widget", - [AC_WID_VOL_KNB] = "Volume Knob Widget", - [AC_WID_BEEP] = "Beep Generator Widget", - [AC_WID_VENDOR] = "Vendor Defined Widget", - }; - if (wid_value == -1) - return "UNKNOWN Widget"; - wid_value &= 0xf; - if (names[wid_value]) - return names[wid_value]; - else - return "UNKNOWN Widget"; -} - -static void print_nid_array(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid, - struct snd_array *array) -{ - int i; - struct hda_nid_item *items = array->list, *item; - struct snd_kcontrol *kctl; - for (i = 0; i < array->used; i++) { - item = &items[i]; - if (item->nid == nid) { - kctl = item->kctl; - snd_iprintf(buffer, - " Control: name=\"%s\", index=%i, device=%i\n", - kctl->id.name, kctl->id.index + item->index, - kctl->id.device); - if (item->flags & HDA_NID_ITEM_AMP) - snd_iprintf(buffer, - " ControlAmp: chs=%lu, dir=%s, " - "idx=%lu, ofs=%lu\n", - get_amp_channels(kctl), - get_amp_direction(kctl) ? "Out" : "In", - get_amp_index(kctl), - get_amp_offset(kctl)); - } - } -} - -static void print_nid_pcms(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid) -{ - int pcm, type; - struct hda_pcm *cpcm; - for (pcm = 0; pcm < codec->num_pcms; pcm++) { - cpcm = &codec->pcm_info[pcm]; - for (type = 0; type < 2; type++) { - if (cpcm->stream[type].nid != nid || cpcm->pcm == NULL) - continue; - snd_iprintf(buffer, " Device: name=\"%s\", " - "type=\"%s\", device=%i\n", - cpcm->name, - snd_hda_pcm_type_name[cpcm->pcm_type], - cpcm->pcm->device); - } - } -} - -static void print_amp_caps(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid, int dir) -{ - unsigned int caps; - caps = snd_hda_param_read(codec, nid, - dir == HDA_OUTPUT ? - AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP); - if (caps == -1 || caps == 0) { - snd_iprintf(buffer, "N/A\n"); - return; - } - snd_iprintf(buffer, "ofs=0x%02x, nsteps=0x%02x, stepsize=0x%02x, " - "mute=%x\n", - caps & AC_AMPCAP_OFFSET, - (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT, - (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT, - (caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT); -} - -static void print_amp_vals(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid, - int dir, int stereo, int indices) -{ - unsigned int val; - int i; - - dir = dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT; - for (i = 0; i < indices; i++) { - snd_iprintf(buffer, " ["); - if (stereo) { - val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_AMP_GAIN_MUTE, - AC_AMP_GET_LEFT | dir | i); - snd_iprintf(buffer, "0x%02x ", val); - } - val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_AMP_GAIN_MUTE, - AC_AMP_GET_RIGHT | dir | i); - snd_iprintf(buffer, "0x%02x]", val); - } - snd_iprintf(buffer, "\n"); -} - -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; - - 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"); -} - -static void print_pcm_bits(struct snd_info_buffer *buffer, unsigned int pcm) -{ - char buf[SND_PRINT_BITS_ADVISED_BUFSIZE]; - - 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, - unsigned int streams) -{ - snd_iprintf(buffer, " formats [0x%x]:", streams & 0xf); - if (streams & AC_SUPFMT_PCM) - snd_iprintf(buffer, " PCM"); - if (streams & AC_SUPFMT_FLOAT32) - snd_iprintf(buffer, " FLOAT"); - if (streams & AC_SUPFMT_AC3) - snd_iprintf(buffer, " AC3"); - snd_iprintf(buffer, "\n"); -} - -static void print_pcm_caps(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int pcm = snd_hda_param_read(codec, nid, AC_PAR_PCM); - unsigned int stream = snd_hda_param_read(codec, nid, AC_PAR_STREAM); - if (pcm == -1 || stream == -1) { - snd_iprintf(buffer, "N/A\n"); - return; - } - print_pcm_rates(buffer, pcm); - print_pcm_bits(buffer, pcm); - print_pcm_formats(buffer, stream); -} - -static const char *get_jack_connection(u32 cfg) -{ - static char *names[16] = { - "Unknown", "1/8", "1/4", "ATAPI", - "RCA", "Optical","Digital", "Analog", - "DIN", "XLR", "RJ11", "Comb", - NULL, NULL, NULL, "Other" - }; - cfg = (cfg & AC_DEFCFG_CONN_TYPE) >> AC_DEFCFG_CONN_TYPE_SHIFT; - if (names[cfg]) - return names[cfg]; - else - return "UNKNOWN"; -} - -static const char *get_jack_color(u32 cfg) -{ - static char *names[16] = { - "Unknown", "Black", "Grey", "Blue", - "Green", "Red", "Orange", "Yellow", - "Purple", "Pink", NULL, NULL, - NULL, NULL, "White", "Other", - }; - cfg = (cfg & AC_DEFCFG_COLOR) >> AC_DEFCFG_COLOR_SHIFT; - if (names[cfg]) - return names[cfg]; - else - return "UNKNOWN"; -} - -static void print_pin_caps(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid, - int *supports_vref) -{ - static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" }; - unsigned int caps, val; - - caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); - snd_iprintf(buffer, " Pincap 0x%08x:", caps); - if (caps & AC_PINCAP_IN) - snd_iprintf(buffer, " IN"); - if (caps & AC_PINCAP_OUT) - snd_iprintf(buffer, " OUT"); - if (caps & AC_PINCAP_HP_DRV) - snd_iprintf(buffer, " HP"); - if (caps & AC_PINCAP_EAPD) - snd_iprintf(buffer, " EAPD"); - if (caps & AC_PINCAP_PRES_DETECT) - snd_iprintf(buffer, " Detect"); - if (caps & AC_PINCAP_BALANCE) - snd_iprintf(buffer, " Balanced"); - if (caps & AC_PINCAP_HDMI) { - /* Realtek uses this bit as a different meaning */ - if ((codec->vendor_id >> 16) == 0x10ec) - snd_iprintf(buffer, " R/L"); - else { - if (caps & AC_PINCAP_HBR) - snd_iprintf(buffer, " HBR"); - snd_iprintf(buffer, " HDMI"); - } - } - if (caps & AC_PINCAP_DP) - snd_iprintf(buffer, " DP"); - if (caps & AC_PINCAP_TRIG_REQ) - snd_iprintf(buffer, " Trigger"); - if (caps & AC_PINCAP_IMP_SENSE) - snd_iprintf(buffer, " ImpSense"); - snd_iprintf(buffer, "\n"); - if (caps & AC_PINCAP_VREF) { - unsigned int vref = - (caps & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; - snd_iprintf(buffer, " Vref caps:"); - if (vref & AC_PINCAP_VREF_HIZ) - snd_iprintf(buffer, " HIZ"); - if (vref & AC_PINCAP_VREF_50) - snd_iprintf(buffer, " 50"); - if (vref & AC_PINCAP_VREF_GRD) - snd_iprintf(buffer, " GRD"); - if (vref & AC_PINCAP_VREF_80) - snd_iprintf(buffer, " 80"); - if (vref & AC_PINCAP_VREF_100) - snd_iprintf(buffer, " 100"); - snd_iprintf(buffer, "\n"); - *supports_vref = 1; - } else - *supports_vref = 0; - if (caps & AC_PINCAP_EAPD) { - val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_EAPD_BTLENABLE, 0); - snd_iprintf(buffer, " EAPD 0x%x:", val); - if (val & AC_EAPDBTL_BALANCED) - snd_iprintf(buffer, " BALANCED"); - if (val & AC_EAPDBTL_EAPD) - snd_iprintf(buffer, " EAPD"); - if (val & AC_EAPDBTL_LR_SWAP) - snd_iprintf(buffer, " R/L"); - snd_iprintf(buffer, "\n"); - } - 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], - 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)); - /* Default association and sequence values refer to default grouping - * of pin complexes and their sequence within the group. This is used - * for priority and resource allocation. - */ - snd_iprintf(buffer, " DefAssociation = 0x%x, Sequence = 0x%x\n", - (caps & AC_DEFCFG_DEF_ASSOC) >> AC_DEFCFG_ASSOC_SHIFT, - caps & AC_DEFCFG_SEQUENCE); - if (((caps & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT) & - AC_DEFCFG_MISC_NO_PRESENCE) { - /* Miscellaneous bit indicates external hardware does not - * support presence detection even if the pin complex - * indicates it is supported. - */ - snd_iprintf(buffer, " Misc = NO_PRESENCE\n"); - } -} - -static void print_pin_ctls(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid, - int supports_vref) -{ - unsigned int pinctls; - - pinctls = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - snd_iprintf(buffer, " Pin-ctls: 0x%02x:", pinctls); - if (pinctls & AC_PINCTL_IN_EN) - snd_iprintf(buffer, " IN"); - if (pinctls & AC_PINCTL_OUT_EN) - snd_iprintf(buffer, " OUT"); - if (pinctls & AC_PINCTL_HP_EN) - snd_iprintf(buffer, " HP"); - if (supports_vref) { - int vref = pinctls & AC_PINCTL_VREFEN; - switch (vref) { - case AC_PINCTL_VREF_HIZ: - snd_iprintf(buffer, " VREF_HIZ"); - break; - case AC_PINCTL_VREF_50: - snd_iprintf(buffer, " VREF_50"); - break; - case AC_PINCTL_VREF_GRD: - snd_iprintf(buffer, " VREF_GRD"); - break; - case AC_PINCTL_VREF_80: - snd_iprintf(buffer, " VREF_80"); - break; - case AC_PINCTL_VREF_100: - snd_iprintf(buffer, " VREF_100"); - break; - } - } - snd_iprintf(buffer, "\n"); -} - -static void print_vol_knob(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int cap = snd_hda_param_read(codec, nid, - AC_PAR_VOL_KNB_CAP); - snd_iprintf(buffer, " Volume-Knob: delta=%d, steps=%d, ", - (cap >> 7) & 1, cap & 0x7f); - cap = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_VOLUME_KNOB_CONTROL, 0); - snd_iprintf(buffer, "direct=%d, val=%d\n", - (cap >> 7) & 1, cap & 0x7f); -} - -static void print_audio_io(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid, - unsigned int wid_type) -{ - int conv = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); - snd_iprintf(buffer, - " Converter: stream=%d, channel=%d\n", - (conv & AC_CONV_STREAM) >> AC_CONV_STREAM_SHIFT, - conv & AC_CONV_CHANNEL); - - if (wid_type == AC_WID_AUD_IN && (conv & AC_CONV_CHANNEL) == 0) { - int sdi = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_SDI_SELECT, 0); - snd_iprintf(buffer, " SDI-Select: %d\n", - sdi & AC_SDI_SELECT); - } -} - -static void print_digital_conv(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int digi1 = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_DIGI_CONVERT_1, 0); - snd_iprintf(buffer, " Digital:"); - if (digi1 & AC_DIG1_ENABLE) - snd_iprintf(buffer, " Enabled"); - if (digi1 & AC_DIG1_V) - snd_iprintf(buffer, " Validity"); - if (digi1 & AC_DIG1_VCFG) - snd_iprintf(buffer, " ValidityCfg"); - if (digi1 & AC_DIG1_EMPHASIS) - snd_iprintf(buffer, " Preemphasis"); - if (digi1 & AC_DIG1_COPYRIGHT) - snd_iprintf(buffer, " Copyright"); - if (digi1 & AC_DIG1_NONAUDIO) - snd_iprintf(buffer, " Non-Audio"); - if (digi1 & AC_DIG1_PROFESSIONAL) - snd_iprintf(buffer, " Pro"); - if (digi1 & AC_DIG1_LEVEL) - snd_iprintf(buffer, " GenLevel"); - snd_iprintf(buffer, "\n"); - snd_iprintf(buffer, " Digital category: 0x%x\n", - (digi1 >> 8) & AC_DIG2_CC); -} - -static const char *get_pwr_state(u32 state) -{ - static const char * const buf[4] = { - "D0", "D1", "D2", "D3" - }; - if (state < 4) - return buf[state]; - return "UNKNOWN"; -} - -static void print_power_state(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid) -{ - static char *names[] = { - [ilog2(AC_PWRST_D0SUP)] = "D0", - [ilog2(AC_PWRST_D1SUP)] = "D1", - [ilog2(AC_PWRST_D2SUP)] = "D2", - [ilog2(AC_PWRST_D3SUP)] = "D3", - [ilog2(AC_PWRST_D3COLDSUP)] = "D3cold", - [ilog2(AC_PWRST_S3D3COLDSUP)] = "S3D3cold", - [ilog2(AC_PWRST_CLKSTOP)] = "CLKSTOP", - [ilog2(AC_PWRST_EPSS)] = "EPSS", - }; - - int sup = snd_hda_param_read(codec, nid, AC_PAR_POWER_STATE); - int pwr = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_POWER_STATE, 0); - if (sup) - snd_iprintf(buffer, " Power states: %s\n", - bits_names(sup, names, ARRAY_SIZE(names))); - - snd_iprintf(buffer, " Power: setting=%s, actual=%s\n", - get_pwr_state(pwr & AC_PWRST_SETTING), - get_pwr_state((pwr & AC_PWRST_ACTUAL) >> - AC_PWRST_ACTUAL_SHIFT)); -} - -static void print_unsol_cap(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid) -{ - int unsol = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_UNSOLICITED_RESPONSE, 0); - snd_iprintf(buffer, - " Unsolicited: tag=%02x, enabled=%d\n", - unsol & AC_UNSOL_TAG, - (unsol & AC_UNSOL_ENABLED) ? 1 : 0); -} - -static void print_proc_caps(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int proc_caps = snd_hda_param_read(codec, nid, - AC_PAR_PROC_CAP); - snd_iprintf(buffer, " Processing caps: benign=%d, ncoeff=%d\n", - proc_caps & AC_PCAP_BENIGN, - (proc_caps & AC_PCAP_NUM_COEF) >> AC_PCAP_NUM_COEF_SHIFT); -} - -static void print_conn_list(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid, - unsigned int wid_type, hda_nid_t *conn, - int conn_len) -{ - int c, curr = -1; - - if (conn_len > 1 && - wid_type != AC_WID_AUD_MIX && - wid_type != AC_WID_VOL_KNB && - wid_type != AC_WID_POWER) - curr = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONNECT_SEL, 0); - snd_iprintf(buffer, " Connection: %d\n", conn_len); - if (conn_len > 0) { - snd_iprintf(buffer, " "); - for (c = 0; c < conn_len; c++) { - snd_iprintf(buffer, " 0x%02x", conn[c]); - if (c == curr) - snd_iprintf(buffer, "*"); - } - snd_iprintf(buffer, "\n"); - } -} - -static void print_gpio(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int gpio = - snd_hda_param_read(codec, codec->afg, AC_PAR_GPIO_CAP); - unsigned int enable, direction, wake, unsol, sticky, data; - int i, max; - snd_iprintf(buffer, "GPIO: io=%d, o=%d, i=%d, " - "unsolicited=%d, wake=%d\n", - gpio & AC_GPIO_IO_COUNT, - (gpio & AC_GPIO_O_COUNT) >> AC_GPIO_O_COUNT_SHIFT, - (gpio & AC_GPIO_I_COUNT) >> AC_GPIO_I_COUNT_SHIFT, - (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, - AC_VERB_GET_GPIO_DIRECTION, 0); - wake = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_GPIO_WAKE_MASK, 0); - unsol = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK, 0); - sticky = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_GPIO_STICKY_MASK, 0); - data = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_GPIO_DATA, 0); - for (i = 0; i < max; ++i) - snd_iprintf(buffer, - " IO[%d]: enable=%d, dir=%d, wake=%d, " - "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, - (unsol & (1<<i)) ? 1 : 0); - /* FIXME: add GPO and GPI pin information */ - print_nid_array(buffer, codec, nid, &codec->mixers); - print_nid_array(buffer, codec, nid, &codec->nids); -} - -static void print_codec_info(struct snd_info_entry *entry, - struct snd_info_buffer *buffer) -{ - struct hda_codec *codec = entry->private_data; - hda_nid_t nid; - int i, nodes; - - snd_iprintf(buffer, "Codec: "); - if (codec->vendor_name && codec->chip_name) - snd_iprintf(buffer, "%s %s\n", - codec->vendor_name, codec->chip_name); - else - snd_iprintf(buffer, "Not Set\n"); - snd_iprintf(buffer, "Address: %d\n", codec->addr); - if (codec->afg) - snd_iprintf(buffer, "AFG Function Id: 0x%x (unsol %u)\n", - codec->afg_function_id, codec->afg_unsol); - if (codec->mfg) - snd_iprintf(buffer, "MFG Function Id: 0x%x (unsol %u)\n", - codec->mfg_function_id, codec->mfg_unsol); - snd_iprintf(buffer, "Vendor Id: 0x%08x\n", codec->vendor_id); - snd_iprintf(buffer, "Subsystem Id: 0x%08x\n", codec->subsystem_id); - snd_iprintf(buffer, "Revision Id: 0x%x\n", codec->revision_id); - - if (codec->mfg) - snd_iprintf(buffer, "Modem Function Group: 0x%x\n", codec->mfg); - else - snd_iprintf(buffer, "No Modem Function Group found\n"); - - if (! codec->afg) - return; - snd_hda_power_up(codec); - snd_iprintf(buffer, "Default PCM:\n"); - print_pcm_caps(buffer, codec, codec->afg); - snd_iprintf(buffer, "Default Amp-In caps: "); - print_amp_caps(buffer, codec, codec->afg, HDA_INPUT); - snd_iprintf(buffer, "Default Amp-Out caps: "); - print_amp_caps(buffer, codec, codec->afg, HDA_OUTPUT); - - nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid); - if (! nid || nodes < 0) { - snd_iprintf(buffer, "Invalid AFG subtree\n"); - snd_hda_power_down(codec); - return; - } - - 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 = - snd_hda_param_read(codec, nid, - AC_PAR_AUDIO_WIDGET_CAP); - unsigned int wid_type = get_wcaps_type(wid_caps); - hda_nid_t conn[HDA_MAX_CONNECTIONS]; - int conn_len = 0; - - snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid, - get_wid_type_name(wid_type), wid_caps); - if (wid_caps & AC_WCAP_STEREO) { - unsigned int chans = get_wcaps_channels(wid_caps); - if (chans == 2) - snd_iprintf(buffer, " Stereo"); - else - snd_iprintf(buffer, " %d-Channels", chans); - } else - snd_iprintf(buffer, " Mono"); - if (wid_caps & AC_WCAP_DIGITAL) - snd_iprintf(buffer, " Digital"); - if (wid_caps & AC_WCAP_IN_AMP) - snd_iprintf(buffer, " Amp-In"); - if (wid_caps & AC_WCAP_OUT_AMP) - snd_iprintf(buffer, " Amp-Out"); - if (wid_caps & AC_WCAP_STRIPE) - snd_iprintf(buffer, " Stripe"); - if (wid_caps & AC_WCAP_LR_SWAP) - snd_iprintf(buffer, " R/L"); - if (wid_caps & AC_WCAP_CP_CAPS) - snd_iprintf(buffer, " CP"); - snd_iprintf(buffer, "\n"); - - print_nid_array(buffer, codec, nid, &codec->mixers); - print_nid_array(buffer, codec, nid, &codec->nids); - print_nid_pcms(buffer, codec, nid); - - /* volume knob is a special widget that always have connection - * list - */ - if (wid_type == AC_WID_VOL_KNB) - wid_caps |= AC_WCAP_CONN_LIST; - - if (wid_caps & AC_WCAP_CONN_LIST) - conn_len = snd_hda_get_raw_connections(codec, nid, conn, - HDA_MAX_CONNECTIONS); - - if (wid_caps & AC_WCAP_IN_AMP) { - snd_iprintf(buffer, " Amp-In caps: "); - print_amp_caps(buffer, codec, nid, HDA_INPUT); - snd_iprintf(buffer, " Amp-In vals: "); - if (wid_type == AC_WID_PIN || - (codec->single_adc_amp && - wid_type == AC_WID_AUD_IN)) - print_amp_vals(buffer, codec, nid, HDA_INPUT, - wid_caps & AC_WCAP_STEREO, - 1); - else - print_amp_vals(buffer, codec, nid, HDA_INPUT, - wid_caps & AC_WCAP_STEREO, - conn_len); - } - if (wid_caps & AC_WCAP_OUT_AMP) { - snd_iprintf(buffer, " Amp-Out caps: "); - print_amp_caps(buffer, codec, nid, HDA_OUTPUT); - snd_iprintf(buffer, " Amp-Out vals: "); - if (wid_type == AC_WID_PIN && - codec->pin_amp_workaround) - print_amp_vals(buffer, codec, nid, HDA_OUTPUT, - wid_caps & AC_WCAP_STEREO, - conn_len); - else - print_amp_vals(buffer, codec, nid, HDA_OUTPUT, - wid_caps & AC_WCAP_STEREO, 1); - } - - switch (wid_type) { - case AC_WID_PIN: { - int supports_vref; - print_pin_caps(buffer, codec, nid, &supports_vref); - print_pin_ctls(buffer, codec, nid, supports_vref); - break; - } - case AC_WID_VOL_KNB: - print_vol_knob(buffer, codec, nid); - break; - case AC_WID_AUD_OUT: - case AC_WID_AUD_IN: - print_audio_io(buffer, codec, nid, wid_type); - if (wid_caps & AC_WCAP_DIGITAL) - print_digital_conv(buffer, codec, nid); - if (wid_caps & AC_WCAP_FORMAT_OVRD) { - snd_iprintf(buffer, " PCM:\n"); - print_pcm_caps(buffer, codec, nid); - } - break; - } - - if (wid_caps & AC_WCAP_UNSOL_CAP) - print_unsol_cap(buffer, codec, nid); - - if (wid_caps & AC_WCAP_POWER) - print_power_state(buffer, codec, nid); - - if (wid_caps & AC_WCAP_DELAY) - snd_iprintf(buffer, " Delay: %d samples\n", - (wid_caps & AC_WCAP_DELAY) >> - AC_WCAP_DELAY_SHIFT); - - if (wid_caps & AC_WCAP_CONN_LIST) - print_conn_list(buffer, codec, nid, wid_type, - conn, conn_len); - - if (wid_caps & AC_WCAP_PROC_WID) - print_proc_caps(buffer, codec, nid); - - if (codec->proc_widget_hook) - codec->proc_widget_hook(buffer, codec, nid); - } - snd_hda_power_down(codec); -} - -/* - * create a proc read - */ -int snd_hda_codec_proc_new(struct hda_codec *codec) -{ - char name[32]; - struct snd_info_entry *entry; - int err; - - snprintf(name, sizeof(name), "codec#%d", codec->addr); - err = snd_card_proc_new(codec->bus->card, name, &entry); - if (err < 0) - return err; - - snd_info_set_text_ops(entry, codec, print_codec_info); - return 0; -} - diff --git a/ANDROID_3.4.5/sound/pci/hda/hda_trace.h b/ANDROID_3.4.5/sound/pci/hda/hda_trace.h deleted file mode 100644 index 9884871d..00000000 --- a/ANDROID_3.4.5/sound/pci/hda/hda_trace.h +++ /dev/null @@ -1,117 +0,0 @@ -#undef TRACE_SYSTEM -#define TRACE_SYSTEM hda -#define TRACE_INCLUDE_FILE hda_trace - -#if !defined(_TRACE_HDA_H) || defined(TRACE_HEADER_MULTI_READ) -#define _TRACE_HDA_H - -#include <linux/tracepoint.h> - -struct hda_bus; -struct hda_codec; - -DECLARE_EVENT_CLASS(hda_cmd, - - TP_PROTO(struct hda_codec *codec, unsigned int val), - - TP_ARGS(codec, val), - - TP_STRUCT__entry( - __field( unsigned int, card ) - __field( unsigned int, addr ) - __field( unsigned int, val ) - ), - - TP_fast_assign( - __entry->card = (codec)->bus->card->number; - __entry->addr = (codec)->addr; - __entry->val = (val); - ), - - TP_printk("[%d:%d] val=%x", __entry->card, __entry->addr, __entry->val) -); - -DEFINE_EVENT(hda_cmd, hda_send_cmd, - TP_PROTO(struct hda_codec *codec, unsigned int val), - TP_ARGS(codec, val) -); - -DEFINE_EVENT(hda_cmd, hda_get_response, - TP_PROTO(struct hda_codec *codec, unsigned int val), - TP_ARGS(codec, val) -); - -TRACE_EVENT(hda_bus_reset, - - TP_PROTO(struct hda_bus *bus), - - TP_ARGS(bus), - - TP_STRUCT__entry( - __field( unsigned int, card ) - ), - - TP_fast_assign( - __entry->card = (bus)->card->number; - ), - - TP_printk("[%d]", __entry->card) -); - -DECLARE_EVENT_CLASS(hda_power, - - TP_PROTO(struct hda_codec *codec), - - TP_ARGS(codec), - - TP_STRUCT__entry( - __field( unsigned int, card ) - __field( unsigned int, addr ) - ), - - TP_fast_assign( - __entry->card = (codec)->bus->card->number; - __entry->addr = (codec)->addr; - ), - - TP_printk("[%d:%d]", __entry->card, __entry->addr) -); - -DEFINE_EVENT(hda_power, hda_power_down, - TP_PROTO(struct hda_codec *codec), - TP_ARGS(codec) -); - -DEFINE_EVENT(hda_power, hda_power_up, - TP_PROTO(struct hda_codec *codec), - TP_ARGS(codec) -); - -TRACE_EVENT(hda_unsol_event, - - TP_PROTO(struct hda_bus *bus, u32 res, u32 res_ex), - - TP_ARGS(bus, res, res_ex), - - TP_STRUCT__entry( - __field( unsigned int, card ) - __field( u32, res ) - __field( u32, res_ex ) - ), - - TP_fast_assign( - __entry->card = (bus)->card->number; - __entry->res = res; - __entry->res_ex = res_ex; - ), - - TP_printk("[%d] res=%x, res_ex=%x", __entry->card, - __entry->res, __entry->res_ex) -); - -#endif /* _TRACE_HDA_H */ - -/* This part must be outside protection */ -#undef TRACE_INCLUDE_PATH -#define TRACE_INCLUDE_PATH . -#include <trace/define_trace.h> diff --git a/ANDROID_3.4.5/sound/pci/hda/patch_analog.c b/ANDROID_3.4.5/sound/pci/hda/patch_analog.c deleted file mode 100644 index 71433939..00000000 --- a/ANDROID_3.4.5/sound/pci/hda/patch_analog.c +++ /dev/null @@ -1,5067 +0,0 @@ -/* - * HD audio interface patch for AD1882, AD1884, AD1981HD, AD1983, AD1984, - * AD1986A, AD1988 - * - * Copyright (c) 2005-2007 Takashi Iwai <tiwai@suse.de> - * - * 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 <linux/delay.h> -#include <linux/slab.h> -#include <linux/pci.h> -#include <linux/module.h> - -#include <sound/core.h> -#include "hda_codec.h" -#include "hda_local.h" -#include "hda_beep.h" -#include "hda_jack.h" - -struct ad198x_spec { - const struct snd_kcontrol_new *mixers[6]; - int num_mixers; - unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */ - const struct hda_verb *init_verbs[6]; /* initialization verbs - * don't forget NULL termination! - */ - unsigned int num_init_verbs; - - /* playback */ - struct hda_multi_out multiout; /* playback set-up - * max_channels, dacs must be set - * dig_out_nid and hp_nid are optional - */ - unsigned int cur_eapd; - unsigned int need_dac_fix; - - const hda_nid_t *alt_dac_nid; - const struct hda_pcm_stream *stream_analog_alt_playback; - int independent_hp; - int num_active_streams; - - /* capture */ - unsigned int num_adc_nids; - const hda_nid_t *adc_nids; - hda_nid_t dig_in_nid; /* digital-in NID; optional */ - - /* capture source */ - const struct hda_input_mux *input_mux; - const hda_nid_t *capsrc_nids; - unsigned int cur_mux[3]; - - /* channel model */ - const struct hda_channel_mode *channel_mode; - int num_channel_mode; - - /* PCM information */ - struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */ - - unsigned int spdif_route; - - /* dynamic controls, init_verbs and input_mux */ - struct auto_pin_cfg autocfg; - struct snd_array kctls; - struct hda_input_mux private_imux; - hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; - - unsigned int jack_present: 1; - unsigned int inv_jack_detect: 1;/* inverted jack-detection */ - unsigned int inv_eapd: 1; /* inverted EAPD implementation */ - unsigned int analog_beep: 1; /* analog beep input present */ - unsigned int avoid_init_slave_vol:1; - -#ifdef CONFIG_SND_HDA_POWER_SAVE - struct hda_loopback_check loopback; -#endif - /* for virtual master */ - hda_nid_t vmaster_nid; - const char * const *slave_vols; - const char * const *slave_sws; -}; - -/* - * input MUX handling (common part) - */ -static int ad198x_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - - return snd_hda_input_mux_info(spec->input_mux, uinfo); -} - -static int ad198x_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - - ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; - return 0; -} - -static int ad198x_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - - return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, - spec->capsrc_nids[adc_idx], - &spec->cur_mux[adc_idx]); -} - -/* - * initialization (common callbacks) - */ -static int ad198x_init(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->num_init_verbs; i++) - snd_hda_sequence_write(codec, spec->init_verbs[i]); - return 0; -} - -static const char * const ad_slave_pfxs[] = { - "Front", "Surround", "Center", "LFE", "Side", - "Headphone", "Mono", "Speaker", "IEC958", - NULL -}; - -static const char * const ad1988_6stack_fp_slave_pfxs[] = { - "Front", "Surround", "Center", "LFE", "Side", "IEC958", - NULL -}; - -static void ad198x_free_kctls(struct hda_codec *codec); - -#ifdef CONFIG_SND_HDA_INPUT_BEEP -/* additional beep mixers; the actual parameters are overwritten at build */ -static const struct snd_kcontrol_new ad_beep_mixer[] = { - HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT), - HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new ad_beep2_mixer[] = { - HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0, 0, HDA_OUTPUT), - HDA_CODEC_MUTE_BEEP("Digital Beep Playback Switch", 0, 0, HDA_OUTPUT), - { } /* end */ -}; - -#define set_beep_amp(spec, nid, idx, dir) \ - ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */ -#else -#define set_beep_amp(spec, nid, idx, dir) /* NOP */ -#endif - -static int ad198x_build_controls(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - struct snd_kcontrol *kctl; - unsigned int i; - int err; - - for (i = 0; i < spec->num_mixers; i++) { - err = snd_hda_add_new_ctls(codec, spec->mixers[i]); - if (err < 0) - return err; - } - if (spec->multiout.dig_out_nid) { - err = snd_hda_create_spdif_out_ctls(codec, - spec->multiout.dig_out_nid, - spec->multiout.dig_out_nid); - if (err < 0) - return err; - err = snd_hda_create_spdif_share_sw(codec, - &spec->multiout); - if (err < 0) - return err; - spec->multiout.share_spdif = 1; - } - if (spec->dig_in_nid) { - err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); - if (err < 0) - return err; - } - - /* create beep controls if needed */ -#ifdef CONFIG_SND_HDA_INPUT_BEEP - if (spec->beep_amp) { - const struct snd_kcontrol_new *knew; - knew = spec->analog_beep ? ad_beep2_mixer : ad_beep_mixer; - for ( ; knew->name; knew++) { - struct snd_kcontrol *kctl; - kctl = snd_ctl_new1(knew, codec); - if (!kctl) - return -ENOMEM; - kctl->private_value = spec->beep_amp; - err = snd_hda_ctl_add(codec, 0, kctl); - if (err < 0) - return err; - } - } -#endif - - /* if we have no master control, let's create it */ - if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { - unsigned int vmaster_tlv[4]; - snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, - HDA_OUTPUT, vmaster_tlv); - err = __snd_hda_add_vmaster(codec, "Master Playback Volume", - vmaster_tlv, - (spec->slave_vols ? - spec->slave_vols : ad_slave_pfxs), - "Playback Volume", - !spec->avoid_init_slave_vol, NULL); - if (err < 0) - return err; - } - if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { - err = snd_hda_add_vmaster(codec, "Master Playback Switch", - NULL, - (spec->slave_sws ? - spec->slave_sws : ad_slave_pfxs), - "Playback Switch"); - if (err < 0) - return err; - } - - ad198x_free_kctls(codec); /* no longer needed */ - - /* assign Capture Source enums to NID */ - kctl = snd_hda_find_mixer_ctl(codec, "Capture Source"); - if (!kctl) - kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); - for (i = 0; kctl && i < kctl->count; i++) { - err = snd_hda_add_nid(codec, kctl, i, spec->capsrc_nids[i]); - if (err < 0) - return err; - } - - /* assign IEC958 enums to NID */ - kctl = snd_hda_find_mixer_ctl(codec, - SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source"); - if (kctl) { - err = snd_hda_add_nid(codec, kctl, 0, - spec->multiout.dig_out_nid); - if (err < 0) - return err; - } - - return 0; -} - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid) -{ - struct ad198x_spec *spec = codec->spec; - return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); -} -#endif - -static void activate_ctl(struct hda_codec *codec, const char *name, int active) -{ - struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name); - if (ctl) { - ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; - ctl->vd[0].access |= active ? 0 : - SNDRV_CTL_ELEM_ACCESS_INACTIVE; - ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_WRITE; - ctl->vd[0].access |= active ? - SNDRV_CTL_ELEM_ACCESS_WRITE : 0; - snd_ctl_notify(codec->bus->card, - SNDRV_CTL_EVENT_MASK_INFO, &ctl->id); - } -} - -static void set_stream_active(struct hda_codec *codec, bool active) -{ - struct ad198x_spec *spec = codec->spec; - if (active) - spec->num_active_streams++; - else - spec->num_active_streams--; - activate_ctl(codec, "Independent HP", spec->num_active_streams == 0); -} - -static int ad1988_independent_hp_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - static const char * const texts[] = { "OFF", "ON", NULL}; - int index; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - index = uinfo->value.enumerated.item; - if (index >= 2) - index = 1; - strcpy(uinfo->value.enumerated.name, texts[index]); - return 0; -} - -static int ad1988_independent_hp_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - ucontrol->value.enumerated.item[0] = spec->independent_hp; - return 0; -} - -static int ad1988_independent_hp_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - unsigned int select = ucontrol->value.enumerated.item[0]; - if (spec->independent_hp != select) { - spec->independent_hp = select; - if (spec->independent_hp) - spec->multiout.hp_nid = 0; - else - spec->multiout.hp_nid = spec->alt_dac_nid[0]; - return 1; - } - return 0; -} - -/* - * Analog playback callbacks - */ -static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ad198x_spec *spec = codec->spec; - int err; - set_stream_active(codec, true); - err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, - hinfo); - if (err < 0) { - set_stream_active(codec, false); - return err; - } - return 0; -} - -static int ad198x_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 ad198x_spec *spec = codec->spec; - return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, - format, substream); -} - -static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ad198x_spec *spec = codec->spec; - return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); -} - -static int ad198x_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - set_stream_active(codec, false); - return 0; -} - -static int ad1988_alt_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ad198x_spec *spec = codec->spec; - if (!spec->independent_hp) - return -EBUSY; - set_stream_active(codec, true); - return 0; -} - -static int ad1988_alt_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - set_stream_active(codec, false); - return 0; -} - -static const struct hda_pcm_stream ad198x_pcm_analog_alt_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .ops = { - .open = ad1988_alt_playback_pcm_open, - .close = ad1988_alt_playback_pcm_close - }, -}; - -/* - * Digital out - */ -static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ad198x_spec *spec = codec->spec; - return snd_hda_multi_out_dig_open(codec, &spec->multiout); -} - -static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ad198x_spec *spec = codec->spec; - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} - -static int ad198x_dig_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 ad198x_spec *spec = codec->spec; - return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, - format, substream); -} - -static int ad198x_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ad198x_spec *spec = codec->spec; - return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); -} - -/* - * Analog capture - */ -static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct ad198x_spec *spec = codec->spec; - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], - stream_tag, 0, format); - return 0; -} - -static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ad198x_spec *spec = codec->spec; - snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); - return 0; -} - -/* - */ -static const struct hda_pcm_stream ad198x_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 6, /* changed later */ - .nid = 0, /* fill later */ - .ops = { - .open = ad198x_playback_pcm_open, - .prepare = ad198x_playback_pcm_prepare, - .cleanup = ad198x_playback_pcm_cleanup, - .close = ad198x_playback_pcm_close - }, -}; - -static const struct hda_pcm_stream ad198x_pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .nid = 0, /* fill later */ - .ops = { - .prepare = ad198x_capture_pcm_prepare, - .cleanup = ad198x_capture_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream ad198x_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .nid = 0, /* fill later */ - .ops = { - .open = ad198x_dig_playback_pcm_open, - .close = ad198x_dig_playback_pcm_close, - .prepare = ad198x_dig_playback_pcm_prepare, - .cleanup = ad198x_dig_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream ad198x_pcm_digital_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in alc_build_pcms */ -}; - -static int ad198x_build_pcms(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; - - codec->num_pcms = 1; - codec->pcm_info = info; - - info->name = "AD198x Analog"; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_analog_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_analog_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; - - if (spec->multiout.dig_out_nid) { - info++; - codec->num_pcms++; - info->name = "AD198x Digital"; - info->pcm_type = HDA_PCM_TYPE_SPDIF; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; - if (spec->dig_in_nid) { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_digital_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; - } - } - - if (spec->alt_dac_nid && spec->stream_analog_alt_playback) { - codec->num_pcms++; - info = spec->pcm_rec + 2; - info->name = "AD198x Headphone"; - info->pcm_type = HDA_PCM_TYPE_AUDIO; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = - *spec->stream_analog_alt_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = - spec->alt_dac_nid[0]; - } - - return 0; -} - -static void ad198x_free_kctls(struct hda_codec *codec) -{ - struct ad198x_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 ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front, - hda_nid_t hp) -{ - struct ad198x_spec *spec = codec->spec; - if (snd_hda_query_pin_caps(codec, front) & AC_PINCAP_EAPD) - snd_hda_codec_write(codec, front, 0, AC_VERB_SET_EAPD_BTLENABLE, - !spec->inv_eapd ? 0x00 : 0x02); - if (snd_hda_query_pin_caps(codec, hp) & AC_PINCAP_EAPD) - snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_EAPD_BTLENABLE, - !spec->inv_eapd ? 0x00 : 0x02); -} - -static void ad198x_power_eapd(struct hda_codec *codec) -{ - /* We currently only handle front, HP */ - switch (codec->vendor_id) { - case 0x11d41882: - case 0x11d4882a: - case 0x11d41884: - case 0x11d41984: - case 0x11d41883: - case 0x11d4184a: - case 0x11d4194a: - case 0x11d4194b: - case 0x11d41988: - case 0x11d4198b: - case 0x11d4989a: - case 0x11d4989b: - ad198x_power_eapd_write(codec, 0x12, 0x11); - break; - case 0x11d41981: - case 0x11d41983: - ad198x_power_eapd_write(codec, 0x05, 0x06); - break; - case 0x11d41986: - ad198x_power_eapd_write(codec, 0x1b, 0x1a); - break; - } -} - -static void ad198x_shutup(struct hda_codec *codec) -{ - snd_hda_shutup_pins(codec); - ad198x_power_eapd(codec); -} - -static void ad198x_free(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - - if (!spec) - return; - - ad198x_shutup(codec); - ad198x_free_kctls(codec); - kfree(spec); - snd_hda_detach_beep_device(codec); -} - -#ifdef CONFIG_PM -static int ad198x_suspend(struct hda_codec *codec, pm_message_t state) -{ - ad198x_shutup(codec); - return 0; -} -#endif - -static const struct hda_codec_ops ad198x_patch_ops = { - .build_controls = ad198x_build_controls, - .build_pcms = ad198x_build_pcms, - .init = ad198x_init, - .free = ad198x_free, -#ifdef CONFIG_SND_HDA_POWER_SAVE - .check_power_status = ad198x_check_power_status, -#endif -#ifdef CONFIG_PM - .suspend = ad198x_suspend, -#endif - .reboot_notify = ad198x_shutup, -}; - - -/* - * EAPD control - * the private value = nid - */ -#define ad198x_eapd_info snd_ctl_boolean_mono_info - -static int ad198x_eapd_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - if (spec->inv_eapd) - ucontrol->value.integer.value[0] = ! spec->cur_eapd; - else - ucontrol->value.integer.value[0] = spec->cur_eapd; - return 0; -} - -static int ad198x_eapd_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - hda_nid_t nid = kcontrol->private_value & 0xff; - unsigned int eapd; - eapd = !!ucontrol->value.integer.value[0]; - if (spec->inv_eapd) - eapd = !eapd; - if (eapd == spec->cur_eapd) - return 0; - spec->cur_eapd = eapd; - snd_hda_codec_write_cache(codec, nid, - 0, AC_VERB_SET_EAPD_BTLENABLE, - eapd ? 0x02 : 0x00); - return 1; -} - -static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo); -static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); - - -/* - * AD1986A specific - */ - -#define AD1986A_SPDIF_OUT 0x02 -#define AD1986A_FRONT_DAC 0x03 -#define AD1986A_SURR_DAC 0x04 -#define AD1986A_CLFE_DAC 0x05 -#define AD1986A_ADC 0x06 - -static const hda_nid_t ad1986a_dac_nids[3] = { - AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC -}; -static const hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC }; -static const hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 }; - -static const struct hda_input_mux ad1986a_capture_source = { - .num_items = 7, - .items = { - { "Mic", 0x0 }, - { "CD", 0x1 }, - { "Aux", 0x3 }, - { "Line", 0x4 }, - { "Mix", 0x5 }, - { "Mono", 0x6 }, - { "Phone", 0x7 }, - }, -}; - - -static const struct hda_bind_ctls ad1986a_bind_pcm_vol = { - .ops = &snd_hda_bind_vol, - .values = { - HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT), - 0 - }, -}; - -static const struct hda_bind_ctls ad1986a_bind_pcm_sw = { - .ops = &snd_hda_bind_sw, - .values = { - HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT), - 0 - }, -}; - -/* - * mixers - */ -static const struct snd_kcontrol_new ad1986a_mixers[] = { - /* - * bind volumes/mutes of 3 DACs as a single PCM control for simplicity - */ - HDA_BIND_VOL("PCM Playback Volume", &ad1986a_bind_pcm_vol), - HDA_BIND_SW("PCM Playback Switch", &ad1986a_bind_pcm_sw), - HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 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 Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 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, - }, - HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -/* additional mixers for 3stack mode */ -static const struct snd_kcontrol_new ad1986a_3st_mixers[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = ad198x_ch_mode_info, - .get = ad198x_ch_mode_get, - .put = ad198x_ch_mode_put, - }, - { } /* end */ -}; - -/* laptop model - 2ch only */ -static const hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC }; - -/* master controls both pins 0x1a and 0x1b */ -static const struct hda_bind_ctls ad1986a_laptop_master_vol = { - .ops = &snd_hda_bind_vol, - .values = { - HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT), - 0, - }, -}; - -static const struct hda_bind_ctls ad1986a_laptop_master_sw = { - .ops = &snd_hda_bind_sw, - .values = { - HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT), - 0, - }, -}; - -static const struct snd_kcontrol_new ad1986a_laptop_mixers[] = { - HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT), - HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol), - HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw), - HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 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 Volume", 0x0f, 0x0, HDA_OUTPUT), - /* - HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 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, - }, - { } /* end */ -}; - -/* laptop-eapd model - 2ch only */ - -static const struct hda_input_mux ad1986a_laptop_eapd_capture_source = { - .num_items = 3, - .items = { - { "Mic", 0x0 }, - { "Internal Mic", 0x4 }, - { "Mix", 0x5 }, - }, -}; - -static const struct hda_input_mux ad1986a_automic_capture_source = { - .num_items = 2, - .items = { - { "Mic", 0x0 }, - { "Mix", 0x5 }, - }, -}; - -static const struct snd_kcontrol_new ad1986a_laptop_master_mixers[] = { - HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol), - HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw), - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = { - 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 Volume", 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", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b, - .info = ad198x_eapd_info, - .get = ad198x_eapd_get, - .put = ad198x_eapd_put, - .private_value = 0x1b, /* port-D */ - }, - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1986a_laptop_intmic_mixers[] = { - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT), - { } /* end */ -}; - -/* re-connect the mic boost input according to the jack sensing */ -static void ad1986a_automic(struct hda_codec *codec) -{ - unsigned int present; - present = snd_hda_jack_detect(codec, 0x1f); - /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */ - snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL, - present ? 0 : 2); -} - -#define AD1986A_MIC_EVENT 0x36 - -static void ad1986a_automic_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - if ((res >> 26) != AD1986A_MIC_EVENT) - return; - ad1986a_automic(codec); -} - -static int ad1986a_automic_init(struct hda_codec *codec) -{ - ad198x_init(codec); - ad1986a_automic(codec); - return 0; -} - -/* laptop-automute - 2ch only */ - -static void ad1986a_update_hp(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - unsigned int mute; - - if (spec->jack_present) - mute = HDA_AMP_MUTE; /* mute internal speaker */ - else - /* unmute internal speaker if necessary */ - mute = snd_hda_codec_amp_read(codec, 0x1a, 0, HDA_OUTPUT, 0); - snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0, - HDA_AMP_MUTE, mute); -} - -static void ad1986a_hp_automute(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - - spec->jack_present = snd_hda_jack_detect(codec, 0x1a); - if (spec->inv_jack_detect) - spec->jack_present = !spec->jack_present; - ad1986a_update_hp(codec); -} - -#define AD1986A_HP_EVENT 0x37 - -static void ad1986a_hp_unsol_event(struct hda_codec *codec, unsigned int res) -{ - if ((res >> 26) != AD1986A_HP_EVENT) - return; - ad1986a_hp_automute(codec); -} - -static int ad1986a_hp_init(struct hda_codec *codec) -{ - ad198x_init(codec); - ad1986a_hp_automute(codec); - return 0; -} - -/* bind hp and internal speaker mute (with plug check) */ -static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - long *valp = ucontrol->value.integer.value; - int change; - - change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0, - HDA_AMP_MUTE, - valp[0] ? 0 : HDA_AMP_MUTE); - change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0, - HDA_AMP_MUTE, - valp[1] ? 0 : HDA_AMP_MUTE); - if (change) - ad1986a_update_hp(codec); - return change; -} - -static const struct snd_kcontrol_new ad1986a_automute_master_mixers[] = { - HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_AMP_FLAG, - .info = snd_hda_mixer_amp_switch_info, - .get = snd_hda_mixer_amp_switch_get, - .put = ad1986a_hp_master_sw_put, - .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT), - }, - { } /* end */ -}; - - -/* - * initialization verbs - */ -static const struct hda_verb ad1986a_init_verbs[] = { - /* Front, Surround, CLFE DAC; mute as default */ - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* Downmix - off */ - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* HP, Line-Out, Surround, CLFE selectors */ - {0x0a, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Mono selector */ - {0x0e, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Mic selector: Mic 1/2 pin */ - {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Line-in selector: Line-in */ - {0x10, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Mic 1/2 swap */ - {0x11, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Record selector: mic */ - {0x12, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Mic, Phone, CD, Aux, Line-In amp; mute as default */ - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* PC beep */ - {0x18, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */ - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* HP Pin */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, - /* Front, Surround, CLFE Pins */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - /* Mono Pin */ - {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - /* Mic Pin */ - {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - /* Line, Aux, CD, Beep-In Pin */ - {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - { } /* end */ -}; - -static const struct hda_verb ad1986a_ch2_init[] = { - /* Surround out -> Line In */ - { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - /* Line-in selectors */ - { 0x10, AC_VERB_SET_CONNECT_SEL, 0x1 }, - /* CLFE -> Mic in */ - { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - /* Mic selector, mix C/LFE (backmic) and Mic (frontmic) */ - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 }, - { } /* end */ -}; - -static const struct hda_verb ad1986a_ch4_init[] = { - /* Surround out -> Surround */ - { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 }, - /* CLFE -> Mic in */ - { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 }, - { } /* end */ -}; - -static const struct hda_verb ad1986a_ch6_init[] = { - /* Surround out -> Surround out */ - { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 }, - /* CLFE -> CLFE */ - { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x0 }, - { } /* end */ -}; - -static const struct hda_channel_mode ad1986a_modes[3] = { - { 2, ad1986a_ch2_init }, - { 4, ad1986a_ch4_init }, - { 6, ad1986a_ch6_init }, -}; - -/* eapd initialization */ -static const struct hda_verb ad1986a_eapd_init_verbs[] = { - {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, - {} -}; - -static const struct hda_verb ad1986a_automic_verbs[] = { - {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - /*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/ - {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT}, - {} -}; - -/* Ultra initialization */ -static const struct hda_verb ad1986a_ultra_init[] = { - /* eapd initialization */ - { 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, - /* CLFE -> Mic in */ - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 }, - { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - { 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 }, - { } /* end */ -}; - -/* pin sensing on HP jack */ -static const struct hda_verb ad1986a_hp_init_verbs[] = { - {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT}, - {} -}; - -static void ad1986a_samsung_p50_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - switch (res >> 26) { - case AD1986A_HP_EVENT: - ad1986a_hp_automute(codec); - break; - case AD1986A_MIC_EVENT: - ad1986a_automic(codec); - break; - } -} - -static int ad1986a_samsung_p50_init(struct hda_codec *codec) -{ - ad198x_init(codec); - ad1986a_hp_automute(codec); - ad1986a_automic(codec); - return 0; -} - - -/* models */ -enum { - AD1986A_6STACK, - AD1986A_3STACK, - AD1986A_LAPTOP, - AD1986A_LAPTOP_EAPD, - AD1986A_LAPTOP_AUTOMUTE, - AD1986A_ULTRA, - AD1986A_SAMSUNG, - AD1986A_SAMSUNG_P50, - AD1986A_MODELS -}; - -static const char * const ad1986a_models[AD1986A_MODELS] = { - [AD1986A_6STACK] = "6stack", - [AD1986A_3STACK] = "3stack", - [AD1986A_LAPTOP] = "laptop", - [AD1986A_LAPTOP_EAPD] = "laptop-eapd", - [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute", - [AD1986A_ULTRA] = "ultra", - [AD1986A_SAMSUNG] = "samsung", - [AD1986A_SAMSUNG_P50] = "samsung-p50", -}; - -static const struct snd_pci_quirk ad1986a_cfg_tbl[] = { - SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP), - SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK), - SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK), - SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP), - SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK), - SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK), - SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK), - SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK), - SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba Satellite L40-10Q", AD1986A_3STACK), - SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK), - SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP), - SND_PCI_QUIRK(0x144d, 0xc024, "Samsung P50", AD1986A_SAMSUNG_P50), - SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA), - SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_SAMSUNG), - SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK), - SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP), - SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK), - SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE), - SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP), - {} -}; - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list ad1986a_loopbacks[] = { - { 0x13, HDA_OUTPUT, 0 }, /* Mic */ - { 0x14, HDA_OUTPUT, 0 }, /* Phone */ - { 0x15, HDA_OUTPUT, 0 }, /* CD */ - { 0x16, HDA_OUTPUT, 0 }, /* Aux */ - { 0x17, HDA_OUTPUT, 0 }, /* Line */ - { } /* end */ -}; -#endif - -static int is_jack_available(struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int conf = snd_hda_codec_get_pincfg(codec, nid); - return get_defcfg_connect(conf) != AC_JACK_PORT_NONE; -} - -static int patch_ad1986a(struct hda_codec *codec) -{ - struct ad198x_spec *spec; - int err, board_config; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - - err = snd_hda_attach_beep_device(codec, 0x19); - if (err < 0) { - ad198x_free(codec); - return err; - } - set_beep_amp(spec, 0x18, 0, HDA_OUTPUT); - - spec->multiout.max_channels = 6; - spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids); - spec->multiout.dac_nids = ad1986a_dac_nids; - spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT; - spec->num_adc_nids = 1; - spec->adc_nids = ad1986a_adc_nids; - spec->capsrc_nids = ad1986a_capsrc_nids; - spec->input_mux = &ad1986a_capture_source; - spec->num_mixers = 1; - spec->mixers[0] = ad1986a_mixers; - spec->num_init_verbs = 1; - spec->init_verbs[0] = ad1986a_init_verbs; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = ad1986a_loopbacks; -#endif - spec->vmaster_nid = 0x1b; - spec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */ - - codec->patch_ops = ad198x_patch_ops; - - /* override some parameters */ - board_config = snd_hda_check_board_config(codec, AD1986A_MODELS, - ad1986a_models, - ad1986a_cfg_tbl); - switch (board_config) { - case AD1986A_3STACK: - spec->num_mixers = 2; - spec->mixers[1] = ad1986a_3st_mixers; - spec->num_init_verbs = 2; - spec->init_verbs[1] = ad1986a_ch2_init; - spec->channel_mode = ad1986a_modes; - spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes); - spec->need_dac_fix = 1; - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = 1; - break; - case AD1986A_LAPTOP: - spec->mixers[0] = ad1986a_laptop_mixers; - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = ad1986a_laptop_dac_nids; - break; - case AD1986A_LAPTOP_EAPD: - spec->num_mixers = 3; - spec->mixers[0] = ad1986a_laptop_master_mixers; - spec->mixers[1] = ad1986a_laptop_eapd_mixers; - spec->mixers[2] = ad1986a_laptop_intmic_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->num_mixers = 2; - spec->mixers[0] = ad1986a_laptop_master_mixers; - spec->mixers[1] = ad1986a_laptop_eapd_mixers; - spec->num_init_verbs = 3; - spec->init_verbs[1] = ad1986a_eapd_init_verbs; - spec->init_verbs[2] = ad1986a_automic_verbs; - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = ad1986a_laptop_dac_nids; - if (!is_jack_available(codec, 0x25)) - spec->multiout.dig_out_nid = 0; - spec->input_mux = &ad1986a_automic_capture_source; - codec->patch_ops.unsol_event = ad1986a_automic_unsol_event; - codec->patch_ops.init = ad1986a_automic_init; - break; - case AD1986A_SAMSUNG_P50: - spec->num_mixers = 2; - spec->mixers[0] = ad1986a_automute_master_mixers; - spec->mixers[1] = ad1986a_laptop_eapd_mixers; - spec->num_init_verbs = 4; - spec->init_verbs[1] = ad1986a_eapd_init_verbs; - spec->init_verbs[2] = ad1986a_automic_verbs; - spec->init_verbs[3] = ad1986a_hp_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_automic_capture_source; - codec->patch_ops.unsol_event = ad1986a_samsung_p50_unsol_event; - codec->patch_ops.init = ad1986a_samsung_p50_init; - break; - case AD1986A_LAPTOP_AUTOMUTE: - spec->num_mixers = 3; - spec->mixers[0] = ad1986a_automute_master_mixers; - spec->mixers[1] = ad1986a_laptop_eapd_mixers; - spec->mixers[2] = ad1986a_laptop_intmic_mixers; - spec->num_init_verbs = 3; - spec->init_verbs[1] = ad1986a_eapd_init_verbs; - spec->init_verbs[2] = ad1986a_hp_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; - codec->patch_ops.unsol_event = ad1986a_hp_unsol_event; - codec->patch_ops.init = ad1986a_hp_init; - /* Lenovo N100 seems to report the reversed bit - * for HP jack-sensing - */ - spec->inv_jack_detect = 1; - break; - case AD1986A_ULTRA: - spec->mixers[0] = ad1986a_laptop_eapd_mixers; - spec->num_init_verbs = 2; - spec->init_verbs[1] = ad1986a_ultra_init; - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = ad1986a_laptop_dac_nids; - spec->multiout.dig_out_nid = 0; - break; - } - - /* AD1986A has a hardware problem that it can't share a stream - * with multiple output pins. The copy of front to surrounds - * causes noisy or silent outputs at a certain timing, e.g. - * changing the volume. - * So, let's disable the shared stream. - */ - spec->multiout.no_share_stream = 1; - - codec->no_trigger_sense = 1; - codec->no_sticky_stream = 1; - - return 0; -} - -/* - * AD1983 specific - */ - -#define AD1983_SPDIF_OUT 0x02 -#define AD1983_DAC 0x03 -#define AD1983_ADC 0x04 - -static const hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC }; -static const hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC }; -static const hda_nid_t ad1983_capsrc_nids[1] = { 0x15 }; - -static const struct hda_input_mux ad1983_capture_source = { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Line", 0x1 }, - { "Mix", 0x2 }, - { "Mix Mono", 0x3 }, - }, -}; - -/* - * SPDIF playback route - */ -static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - static const char * const texts[] = { "PCM", "ADC" }; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; -} - -static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - - ucontrol->value.enumerated.item[0] = spec->spdif_route; - return 0; -} - -static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - - if (ucontrol->value.enumerated.item[0] > 1) - return -EINVAL; - if (spec->spdif_route != ucontrol->value.enumerated.item[0]) { - spec->spdif_route = ucontrol->value.enumerated.item[0]; - snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0, - AC_VERB_SET_CONNECT_SEL, - spec->spdif_route); - return 1; - } - return 0; -} - -static const struct snd_kcontrol_new ad1983_mixers[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x15, 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 = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", - .info = ad1983_spdif_route_info, - .get = ad1983_spdif_route_get, - .put = ad1983_spdif_route_put, - }, - { } /* end */ -}; - -static const struct hda_verb ad1983_init_verbs[] = { - /* Front, HP, Mono; mute as default */ - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* Beep, PCM, Mic, Line-In: mute */ - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* Front, HP selectors; from Mix */ - {0x05, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x06, AC_VERB_SET_CONNECT_SEL, 0x01}, - /* Mono selector; from Mix */ - {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03}, - /* Mic selector; Mic */ - {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Line-in selector: Line-in */ - {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Mic boost: 0dB */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - /* Record selector: mic */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* SPDIF route: PCM */ - {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Front Pin */ - {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - /* HP Pin */ - {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, - /* Mono Pin */ - {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - /* Mic Pin */ - {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - /* Line Pin */ - {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - { } /* end */ -}; - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list ad1983_loopbacks[] = { - { 0x12, HDA_OUTPUT, 0 }, /* Mic */ - { 0x13, HDA_OUTPUT, 0 }, /* Line */ - { } /* end */ -}; -#endif - -static int patch_ad1983(struct hda_codec *codec) -{ - struct ad198x_spec *spec; - int err; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - - err = snd_hda_attach_beep_device(codec, 0x10); - if (err < 0) { - ad198x_free(codec); - return err; - } - set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); - - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids); - spec->multiout.dac_nids = ad1983_dac_nids; - spec->multiout.dig_out_nid = AD1983_SPDIF_OUT; - spec->num_adc_nids = 1; - spec->adc_nids = ad1983_adc_nids; - spec->capsrc_nids = ad1983_capsrc_nids; - spec->input_mux = &ad1983_capture_source; - spec->num_mixers = 1; - spec->mixers[0] = ad1983_mixers; - spec->num_init_verbs = 1; - spec->init_verbs[0] = ad1983_init_verbs; - spec->spdif_route = 0; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = ad1983_loopbacks; -#endif - spec->vmaster_nid = 0x05; - - codec->patch_ops = ad198x_patch_ops; - - codec->no_trigger_sense = 1; - codec->no_sticky_stream = 1; - - return 0; -} - - -/* - * AD1981 HD specific - */ - -#define AD1981_SPDIF_OUT 0x02 -#define AD1981_DAC 0x03 -#define AD1981_ADC 0x04 - -static const hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC }; -static const hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC }; -static const hda_nid_t ad1981_capsrc_nids[1] = { 0x15 }; - -/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */ -static const struct hda_input_mux ad1981_capture_source = { - .num_items = 7, - .items = { - { "Front Mic", 0x0 }, - { "Line", 0x1 }, - { "Mix", 0x2 }, - { "Mix Mono", 0x3 }, - { "CD", 0x4 }, - { "Mic", 0x6 }, - { "Aux", 0x7 }, - }, -}; - -static const struct snd_kcontrol_new ad1981_mixers[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x15, 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, - }, - /* identical with AD1983 */ - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", - .info = ad1983_spdif_route_info, - .get = ad1983_spdif_route_get, - .put = ad1983_spdif_route_put, - }, - { } /* end */ -}; - -static const struct hda_verb ad1981_init_verbs[] = { - /* Front, HP, Mono; mute as default */ - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */ - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* Front, HP selectors; from Mix */ - {0x05, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x06, AC_VERB_SET_CONNECT_SEL, 0x01}, - /* Mono selector; from Mix */ - {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03}, - /* Mic Mixer; select Front Mic */ - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* Mic boost: 0dB */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Record selector: Front mic */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* SPDIF route: PCM */ - {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Front Pin */ - {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - /* HP Pin */ - {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, - /* Mono Pin */ - {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - /* Front & Rear Mic Pins */ - {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - /* Line Pin */ - {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - /* Digital Beep */ - {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* Line-Out as Input: disabled */ - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - { } /* end */ -}; - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list ad1981_loopbacks[] = { - { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */ - { 0x13, HDA_OUTPUT, 0 }, /* Line */ - { 0x1b, HDA_OUTPUT, 0 }, /* Aux */ - { 0x1c, HDA_OUTPUT, 0 }, /* Mic */ - { 0x1d, HDA_OUTPUT, 0 }, /* CD */ - { } /* end */ -}; -#endif - -/* - * Patch for HP nx6320 - * - * nx6320 uses EAPD in the reverse way - EAPD-on means the internal - * speaker output enabled _and_ mute-LED off. - */ - -#define AD1981_HP_EVENT 0x37 -#define AD1981_MIC_EVENT 0x38 - -static const struct hda_verb ad1981_hp_init_verbs[] = { - {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, /* default off */ - /* pin sensing on HP and Mic jacks */ - {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT}, - {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT}, - {} -}; - -/* turn on/off EAPD (+ mute HP) as a master switch */ -static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - - if (! ad198x_eapd_put(kcontrol, ucontrol)) - return 0; - /* change speaker pin appropriately */ - snd_hda_codec_write(codec, 0x05, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - spec->cur_eapd ? PIN_OUT : 0); - /* toggle HP mute appropriately */ - snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0, - HDA_AMP_MUTE, - spec->cur_eapd ? 0 : HDA_AMP_MUTE); - return 1; -} - -/* bind volumes of both NID 0x05 and 0x06 */ -static const struct hda_bind_ctls ad1981_hp_bind_master_vol = { - .ops = &snd_hda_bind_vol, - .values = { - HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x06, 3, 0, HDA_OUTPUT), - 0 - }, -}; - -/* mute internal speaker if HP is plugged */ -static void ad1981_hp_automute(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_jack_detect(codec, 0x06); - snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); -} - -/* toggle input of built-in and mic jack appropriately */ -static void ad1981_hp_automic(struct hda_codec *codec) -{ - static const struct hda_verb mic_jack_on[] = { - {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - {} - }; - static const struct hda_verb mic_jack_off[] = { - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - {} - }; - unsigned int present; - - present = snd_hda_jack_detect(codec, 0x08); - if (present) - snd_hda_sequence_write(codec, mic_jack_on); - else - snd_hda_sequence_write(codec, mic_jack_off); -} - -/* unsolicited event for HP jack sensing */ -static void ad1981_hp_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - res >>= 26; - switch (res) { - case AD1981_HP_EVENT: - ad1981_hp_automute(codec); - break; - case AD1981_MIC_EVENT: - ad1981_hp_automic(codec); - break; - } -} - -static const struct hda_input_mux ad1981_hp_capture_source = { - .num_items = 3, - .items = { - { "Mic", 0x0 }, - { "Docking-Station", 0x1 }, - { "Mix", 0x2 }, - }, -}; - -static const struct snd_kcontrol_new ad1981_hp_mixers[] = { - HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .subdevice = HDA_SUBDEV_NID_FLAG | 0x05, - .name = "Master Playback Switch", - .info = ad198x_eapd_info, - .get = ad198x_eapd_get, - .put = ad1981_hp_master_sw_put, - .private_value = 0x05, - }, - HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT), -#if 0 - /* FIXME: analog mic/line loopback doesn't work with my tests... - * (although recording is OK) - */ - HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Docking-Station Playback Volume", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Docking-Station Playback Switch", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT), - /* FIXME: does this laptop have analog CD connection? */ - HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT), -#endif - HDA_CODEC_VOLUME("Mic Boost Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x18, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - { } /* end */ -}; - -/* initialize jack-sensing, too */ -static int ad1981_hp_init(struct hda_codec *codec) -{ - ad198x_init(codec); - ad1981_hp_automute(codec); - ad1981_hp_automic(codec); - return 0; -} - -/* configuration for Toshiba Laptops */ -static const struct hda_verb ad1981_toshiba_init_verbs[] = { - {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x01 }, /* default on */ - /* pin sensing on HP and Mic jacks */ - {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT}, - {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT}, - {} -}; - -static const struct snd_kcontrol_new ad1981_toshiba_mixers[] = { - HDA_CODEC_VOLUME("Amp Volume", 0x1a, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Amp Switch", 0x1a, 0x0, HDA_OUTPUT), - { } -}; - -/* configuration for Lenovo Thinkpad T60 */ -static const struct snd_kcontrol_new ad1981_thinkpad_mixers[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x15, 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, - }, - /* identical with AD1983 */ - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", - .info = ad1983_spdif_route_info, - .get = ad1983_spdif_route_get, - .put = ad1983_spdif_route_put, - }, - { } /* end */ -}; - -static const struct hda_input_mux ad1981_thinkpad_capture_source = { - .num_items = 3, - .items = { - { "Mic", 0x0 }, - { "Mix", 0x2 }, - { "CD", 0x4 }, - }, -}; - -/* models */ -enum { - AD1981_BASIC, - AD1981_HP, - AD1981_THINKPAD, - AD1981_TOSHIBA, - AD1981_MODELS -}; - -static const char * const ad1981_models[AD1981_MODELS] = { - [AD1981_HP] = "hp", - [AD1981_THINKPAD] = "thinkpad", - [AD1981_BASIC] = "basic", - [AD1981_TOSHIBA] = "toshiba" -}; - -static const struct snd_pci_quirk ad1981_cfg_tbl[] = { - SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD), - SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD), - /* All HP models */ - SND_PCI_QUIRK_VENDOR(0x103c, "HP nx", AD1981_HP), - SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA), - /* Lenovo Thinkpad T60/X60/Z6xx */ - SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1981_THINKPAD), - /* HP nx6320 (reversed SSID, H/W bug) */ - SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP), - {} -}; - -static int patch_ad1981(struct hda_codec *codec) -{ - struct ad198x_spec *spec; - int err, board_config; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - - err = snd_hda_attach_beep_device(codec, 0x10); - if (err < 0) { - ad198x_free(codec); - return err; - } - set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT); - - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids); - spec->multiout.dac_nids = ad1981_dac_nids; - spec->multiout.dig_out_nid = AD1981_SPDIF_OUT; - spec->num_adc_nids = 1; - spec->adc_nids = ad1981_adc_nids; - spec->capsrc_nids = ad1981_capsrc_nids; - spec->input_mux = &ad1981_capture_source; - spec->num_mixers = 1; - spec->mixers[0] = ad1981_mixers; - spec->num_init_verbs = 1; - spec->init_verbs[0] = ad1981_init_verbs; - spec->spdif_route = 0; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = ad1981_loopbacks; -#endif - spec->vmaster_nid = 0x05; - - codec->patch_ops = ad198x_patch_ops; - - /* override some parameters */ - board_config = snd_hda_check_board_config(codec, AD1981_MODELS, - ad1981_models, - ad1981_cfg_tbl); - switch (board_config) { - case AD1981_HP: - spec->mixers[0] = ad1981_hp_mixers; - spec->num_init_verbs = 2; - spec->init_verbs[1] = ad1981_hp_init_verbs; - if (!is_jack_available(codec, 0x0a)) - spec->multiout.dig_out_nid = 0; - spec->input_mux = &ad1981_hp_capture_source; - - codec->patch_ops.init = ad1981_hp_init; - codec->patch_ops.unsol_event = ad1981_hp_unsol_event; - /* set the upper-limit for mixer amp to 0dB for avoiding the - * possible damage by overloading - */ - snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT, - (0x17 << AC_AMPCAP_OFFSET_SHIFT) | - (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (1 << AC_AMPCAP_MUTE_SHIFT)); - break; - case AD1981_THINKPAD: - spec->mixers[0] = ad1981_thinkpad_mixers; - spec->input_mux = &ad1981_thinkpad_capture_source; - /* set the upper-limit for mixer amp to 0dB for avoiding the - * possible damage by overloading - */ - snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT, - (0x17 << AC_AMPCAP_OFFSET_SHIFT) | - (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (1 << AC_AMPCAP_MUTE_SHIFT)); - break; - case AD1981_TOSHIBA: - spec->mixers[0] = ad1981_hp_mixers; - spec->mixers[1] = ad1981_toshiba_mixers; - spec->num_init_verbs = 2; - spec->init_verbs[1] = ad1981_toshiba_init_verbs; - spec->multiout.dig_out_nid = 0; - spec->input_mux = &ad1981_hp_capture_source; - codec->patch_ops.init = ad1981_hp_init; - codec->patch_ops.unsol_event = ad1981_hp_unsol_event; - break; - } - - codec->no_trigger_sense = 1; - codec->no_sticky_stream = 1; - - return 0; -} - - -/* - * AD1988 - * - * Output pins and routes - * - * Pin Mix Sel DAC (*) - * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06 - * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06 - * port-C 0x15 (mute) <- 0x2c <- 0x31 <- 05/0a - * port-D 0x12 (mute/hp) <- 0x29 <- 04 - * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a - * port-F 0x16 (mute) <- 0x2a <- 06 - * port-G 0x24 (mute) <- 0x27 <- 05 - * port-H 0x25 (mute) <- 0x28 <- 0a - * mono 0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06 - * - * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah - * (*) DAC2/3/4 are swapped to DAC3/4/2 on AD198A rev.2 due to a h/w bug. - * - * Input pins and routes - * - * pin boost mix input # / adc input # - * port-A 0x11 -> 0x38 -> mix 2, ADC 0 - * port-B 0x14 -> 0x39 -> mix 0, ADC 1 - * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2 - * port-D 0x12 -> 0x3d -> mix 3, ADC 8 - * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4 - * port-F 0x16 -> 0x3b -> mix 5, ADC 3 - * port-G 0x24 -> N/A -> 33:1 - mix 1, 34:1 - mix 4, ADC 6 - * port-H 0x25 -> N/A -> 33:2 - mix 1, 34:2 - mix 4, ADC 7 - * - * - * DAC assignment - * 6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03 - * 3stack - front/surr/CLFE/opt DACs - 04/05/0a/03 - * - * Inputs of Analog Mix (0x20) - * 0:Port-B (front mic) - * 1:Port-C/G/H (line-in) - * 2:Port-A - * 3:Port-D (line-in/2) - * 4:Port-E/G/H (mic-in) - * 5:Port-F (mic2-in) - * 6:CD - * 7:Beep - * - * ADC selection - * 0:Port-A - * 1:Port-B (front mic-in) - * 2:Port-C (line-in) - * 3:Port-F (mic2-in) - * 4:Port-E (mic-in) - * 5:CD - * 6:Port-G - * 7:Port-H - * 8:Port-D (line-in/2) - * 9:Mix - * - * Proposed pin assignments by the datasheet - * - * 6-stack - * Port-A front headphone - * B front mic-in - * C rear line-in - * D rear front-out - * E rear mic-in - * F rear surround - * G rear CLFE - * H rear side - * - * 3-stack - * Port-A front headphone - * B front mic - * C rear line-in/surround - * D rear front-out - * E rear mic-in/CLFE - * - * laptop - * Port-A headphone - * B mic-in - * C docking station - * D internal speaker (with EAPD) - * E/F quad mic array - */ - - -/* models */ -enum { - AD1988_6STACK, - AD1988_6STACK_DIG, - AD1988_3STACK, - AD1988_3STACK_DIG, - AD1988_LAPTOP, - AD1988_LAPTOP_DIG, - AD1988_AUTO, - AD1988_MODEL_LAST, -}; - -/* reivision id to check workarounds */ -#define AD1988A_REV2 0x100200 - -#define is_rev2(codec) \ - ((codec)->vendor_id == 0x11d41988 && \ - (codec)->revision_id == AD1988A_REV2) - -/* - * mixers - */ - -static const hda_nid_t ad1988_6stack_dac_nids[4] = { - 0x04, 0x06, 0x05, 0x0a -}; - -static const hda_nid_t ad1988_3stack_dac_nids[3] = { - 0x04, 0x05, 0x0a -}; - -/* for AD1988A revision-2, DAC2-4 are swapped */ -static const hda_nid_t ad1988_6stack_dac_nids_rev2[4] = { - 0x04, 0x05, 0x0a, 0x06 -}; - -static const hda_nid_t ad1988_alt_dac_nid[1] = { - 0x03 -}; - -static const hda_nid_t ad1988_3stack_dac_nids_rev2[3] = { - 0x04, 0x0a, 0x06 -}; - -static const hda_nid_t ad1988_adc_nids[3] = { - 0x08, 0x09, 0x0f -}; - -static const hda_nid_t ad1988_capsrc_nids[3] = { - 0x0c, 0x0d, 0x0e -}; - -#define AD1988_SPDIF_OUT 0x02 -#define AD1988_SPDIF_OUT_HDMI 0x0b -#define AD1988_SPDIF_IN 0x07 - -static const hda_nid_t ad1989b_slave_dig_outs[] = { - AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI, 0 -}; - -static const struct hda_input_mux ad1988_6stack_capture_source = { - .num_items = 5, - .items = { - { "Front Mic", 0x1 }, /* port-B */ - { "Line", 0x2 }, /* port-C */ - { "Mic", 0x4 }, /* port-E */ - { "CD", 0x5 }, - { "Mix", 0x9 }, - }, -}; - -static const struct hda_input_mux ad1988_laptop_capture_source = { - .num_items = 3, - .items = { - { "Mic/Line", 0x1 }, /* port-B */ - { "CD", 0x5 }, - { "Mix", 0x9 }, - }, -}; - -/* - */ -static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode, - spec->num_channel_mode); -} - -static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode, - spec->num_channel_mode, spec->multiout.max_channels); -} - -static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode, - spec->num_channel_mode, - &spec->multiout.max_channels); - if (err >= 0 && spec->need_dac_fix) - spec->multiout.num_dacs = spec->multiout.max_channels / 2; - return err; -} - -static const struct snd_kcontrol_new ad1988_hp_mixers[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Independent HP", - .info = ad1988_independent_hp_info, - .get = ad1988_independent_hp_get, - .put = ad1988_independent_hp_put, - }, - { } /* end */ -}; - -/* 6-stack mode */ -static const struct snd_kcontrol_new ad1988_6stack_mixers1[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1988_6stack_mixers2[] = { - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT), - HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT), - HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT), - HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT), - - HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT), - - HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT), - - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -/* 3-stack mode */ -static const struct snd_kcontrol_new ad1988_3stack_mixers1[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1988_3stack_mixers2[] = { - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT), - HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT), - HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT), - - HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT), - - HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT), - - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = ad198x_ch_mode_info, - .get = ad198x_ch_mode_get, - .put = ad198x_ch_mode_put, - }, - - { } /* end */ -}; - -/* laptop mode */ -static const struct snd_kcontrol_new ad1988_laptop_mixers[] = { - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT), - HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT), - - HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT), - - HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT), - - HDA_CODEC_VOLUME("Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT), - - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "External Amplifier", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x12, - .info = ad198x_eapd_info, - .get = ad198x_eapd_get, - .put = ad198x_eapd_put, - .private_value = 0x12, /* port-D */ - }, - - { } /* end */ -}; - -/* capture */ -static const struct snd_kcontrol_new ad1988_capture_mixers[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 3, - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - { } /* end */ -}; - -static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - static const char * const texts[] = { - "PCM", "ADC1", "ADC2", "ADC3" - }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item >= 4) - uinfo->value.enumerated.item = 3; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; -} - -static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned int sel; - - sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE, - AC_AMP_GET_INPUT); - if (!(sel & 0x80)) - ucontrol->value.enumerated.item[0] = 0; - else { - sel = snd_hda_codec_read(codec, 0x0b, 0, - AC_VERB_GET_CONNECT_SEL, 0); - if (sel < 3) - sel++; - else - sel = 0; - ucontrol->value.enumerated.item[0] = sel; - } - return 0; -} - -static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned int val, sel; - int change; - - val = ucontrol->value.enumerated.item[0]; - if (val > 3) - return -EINVAL; - if (!val) { - sel = snd_hda_codec_read(codec, 0x1d, 0, - AC_VERB_GET_AMP_GAIN_MUTE, - AC_AMP_GET_INPUT); - change = sel & 0x80; - if (change) { - snd_hda_codec_write_cache(codec, 0x1d, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(0)); - snd_hda_codec_write_cache(codec, 0x1d, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_MUTE(1)); - } - } else { - sel = snd_hda_codec_read(codec, 0x1d, 0, - AC_VERB_GET_AMP_GAIN_MUTE, - AC_AMP_GET_INPUT | 0x01); - change = sel & 0x80; - if (change) { - snd_hda_codec_write_cache(codec, 0x1d, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_MUTE(0)); - snd_hda_codec_write_cache(codec, 0x1d, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(1)); - } - sel = snd_hda_codec_read(codec, 0x0b, 0, - AC_VERB_GET_CONNECT_SEL, 0) + 1; - change |= sel != val; - if (change) - snd_hda_codec_write_cache(codec, 0x0b, 0, - AC_VERB_SET_CONNECT_SEL, - val - 1); - } - return change; -} - -static const struct snd_kcontrol_new ad1988_spdif_out_mixers[] = { - HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "IEC958 Playback Source", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b, - .info = ad1988_spdif_playback_source_info, - .get = ad1988_spdif_playback_source_get, - .put = ad1988_spdif_playback_source_put, - }, - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1988_spdif_in_mixers[] = { - HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1989_spdif_out_mixers[] = { - HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("HDMI Playback Volume", 0x1d, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -/* - * initialization verbs - */ - -/* - * for 6-stack (+dig) - */ -static const struct hda_verb ad1988_6stack_init_verbs[] = { - /* Front, Surround, CLFE, side DAC; unmute as default */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Port-A front headphon path */ - {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* Port-D line-out path */ - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* Port-F surround path */ - {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* Port-G CLFE path */ - {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* Port-H side path */ - {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* Mono out path */ - {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */ - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */ - /* Port-B front mic-in path */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* Port-C line-in path */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x33, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Port-E mic-in path */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x34, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Analog CD Input */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - /* Analog Mix output amp */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */ - - { } -}; - -static const struct hda_verb ad1988_6stack_fp_init_verbs[] = { - /* Headphone; unmute as default */ - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Port-A front headphon path */ - {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - - { } -}; - -static const struct hda_verb ad1988_capture_init_verbs[] = { - /* mute analog mix */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, - /* select ADCs - front-mic */ - {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1}, - {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1}, - {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1}, - - { } -}; - -static const struct hda_verb ad1988_spdif_init_verbs[] = { - /* SPDIF out sel */ - {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */ - {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */ - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* SPDIF out pin */ - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */ - - { } -}; - -static const struct hda_verb ad1988_spdif_in_init_verbs[] = { - /* unmute SPDIF input pin */ - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - { } -}; - -/* AD1989 has no ADC -> SPDIF route */ -static const struct hda_verb ad1989_spdif_init_verbs[] = { - /* SPDIF-1 out pin */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */ - /* SPDIF-2/HDMI out pin */ - {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */ - { } -}; - -/* - * verbs for 3stack (+dig) - */ -static const struct hda_verb ad1988_3stack_ch2_init[] = { - /* set port-C to line-in */ - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - /* set port-E to mic-in */ - { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { } /* end */ -}; - -static const struct hda_verb ad1988_3stack_ch6_init[] = { - /* set port-C to surround out */ - { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - /* set port-E to CLFE out */ - { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { } /* end */ -}; - -static const struct hda_channel_mode ad1988_3stack_modes[2] = { - { 2, ad1988_3stack_ch2_init }, - { 6, ad1988_3stack_ch6_init }, -}; - -static const struct hda_verb ad1988_3stack_init_verbs[] = { - /* Front, Surround, CLFE, side DAC; unmute as default */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Port-A front headphon path */ - {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* Port-D line-out path */ - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* Mono out path */ - {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */ - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */ - /* Port-B front mic-in path */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* Port-C line-in/surround path - 6ch mode as default */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */ - {0x33, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Port-E mic-in/CLFE path - 6ch mode as default */ - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */ - {0x34, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* mute analog mix */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, - /* select ADCs - front-mic */ - {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1}, - {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1}, - {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1}, - /* Analog Mix output amp */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */ - { } -}; - -/* - * verbs for laptop mode (+dig) - */ -static const struct hda_verb ad1988_laptop_hp_on[] = { - /* unmute port-A and mute port-D */ - { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { } /* end */ -}; -static const struct hda_verb ad1988_laptop_hp_off[] = { - /* mute port-A and unmute port-D */ - { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { } /* end */ -}; - -#define AD1988_HP_EVENT 0x01 - -static const struct hda_verb ad1988_laptop_init_verbs[] = { - /* Front, Surround, CLFE, side DAC; unmute as default */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Port-A front headphon path */ - {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* unsolicited event for pin-sense */ - {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT }, - /* Port-D line-out path + EAPD */ - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */ - /* Mono out path */ - {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */ - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */ - /* Port-B mic-in path */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* Port-C docking station - try to output */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x33, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* mute analog mix */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, - /* select ADCs - mic */ - {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1}, - {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1}, - {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1}, - /* Analog Mix output amp */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */ - { } -}; - -static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res) -{ - if ((res >> 26) != AD1988_HP_EVENT) - return; - if (snd_hda_jack_detect(codec, 0x11)) - snd_hda_sequence_write(codec, ad1988_laptop_hp_on); - else - snd_hda_sequence_write(codec, ad1988_laptop_hp_off); -} - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list ad1988_loopbacks[] = { - { 0x20, HDA_INPUT, 0 }, /* Front Mic */ - { 0x20, HDA_INPUT, 1 }, /* Line */ - { 0x20, HDA_INPUT, 4 }, /* Mic */ - { 0x20, HDA_INPUT, 6 }, /* CD */ - { } /* end */ -}; -#endif - -/* - * Automatic parse of I/O pins from the BIOS configuration - */ - -enum { - AD_CTL_WIDGET_VOL, - AD_CTL_WIDGET_MUTE, - AD_CTL_BIND_MUTE, -}; -static const struct snd_kcontrol_new ad1988_control_templates[] = { - HDA_CODEC_VOLUME(NULL, 0, 0, 0), - HDA_CODEC_MUTE(NULL, 0, 0, 0), - HDA_BIND_MUTE(NULL, 0, 0, 0), -}; - -/* add dynamic controls */ -static int add_control(struct ad198x_spec *spec, int type, const char *name, - unsigned long val) -{ - struct snd_kcontrol_new *knew; - - 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; - if (get_amp_nid_(val)) - knew->subdevice = HDA_SUBDEV_AMP_FLAG; - knew->private_value = val; - return 0; -} - -#define AD1988_PIN_CD_NID 0x18 -#define AD1988_PIN_BEEP_NID 0x10 - -static const hda_nid_t ad1988_mixer_nids[8] = { - /* A B C D E F G H */ - 0x22, 0x2b, 0x2c, 0x29, 0x26, 0x2a, 0x27, 0x28 -}; - -static inline hda_nid_t ad1988_idx_to_dac(struct hda_codec *codec, int idx) -{ - static const hda_nid_t idx_to_dac[8] = { - /* A B C D E F G H */ - 0x03, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a - }; - static const hda_nid_t idx_to_dac_rev2[8] = { - /* A B C D E F G H */ - 0x03, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06 - }; - if (is_rev2(codec)) - return idx_to_dac_rev2[idx]; - else - return idx_to_dac[idx]; -} - -static const hda_nid_t ad1988_boost_nids[8] = { - 0x38, 0x39, 0x3a, 0x3d, 0x3c, 0x3b, 0, 0 -}; - -static int ad1988_pin_idx(hda_nid_t nid) -{ - static const hda_nid_t ad1988_io_pins[8] = { - 0x11, 0x14, 0x15, 0x12, 0x17, 0x16, 0x24, 0x25 - }; - int i; - for (i = 0; i < ARRAY_SIZE(ad1988_io_pins); i++) - if (ad1988_io_pins[i] == nid) - return i; - return 0; /* should be -1 */ -} - -static int ad1988_pin_to_loopback_idx(hda_nid_t nid) -{ - static const int loopback_idx[8] = { - 2, 0, 1, 3, 4, 5, 1, 4 - }; - switch (nid) { - case AD1988_PIN_CD_NID: - return 6; - default: - return loopback_idx[ad1988_pin_idx(nid)]; - } -} - -static int ad1988_pin_to_adc_idx(hda_nid_t nid) -{ - static const int adc_idx[8] = { - 0, 1, 2, 8, 4, 3, 6, 7 - }; - switch (nid) { - case AD1988_PIN_CD_NID: - return 5; - default: - return adc_idx[ad1988_pin_idx(nid)]; - } -} - -/* fill in the dac_nids table from the parsed pin configuration */ -static int ad1988_auto_fill_dac_nids(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - struct ad198x_spec *spec = codec->spec; - int i, idx; - - spec->multiout.dac_nids = spec->private_dac_nids; - - /* check the pins hardwired to audio widget */ - for (i = 0; i < cfg->line_outs; i++) { - idx = ad1988_pin_idx(cfg->line_out_pins[i]); - spec->private_dac_nids[i] = ad1988_idx_to_dac(codec, idx); - } - spec->multiout.num_dacs = cfg->line_outs; - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int ad1988_auto_create_multi_out_ctls(struct ad198x_spec *spec, - const struct auto_pin_cfg *cfg) -{ - char name[32]; - static const char * const chname[4] = { - "Front", "Surround", NULL /*CLFE*/, "Side" - }; - hda_nid_t nid; - int i, err; - - for (i = 0; i < cfg->line_outs; i++) { - hda_nid_t dac = spec->multiout.dac_nids[i]; - if (! dac) - continue; - nid = ad1988_mixer_nids[ad1988_pin_idx(cfg->line_out_pins[i])]; - if (i == 2) { - /* Center/LFE */ - err = add_control(spec, AD_CTL_WIDGET_VOL, - "Center Playback Volume", - HDA_COMPOSE_AMP_VAL(dac, 1, 0, HDA_OUTPUT)); - if (err < 0) - return err; - err = add_control(spec, AD_CTL_WIDGET_VOL, - "LFE Playback Volume", - HDA_COMPOSE_AMP_VAL(dac, 2, 0, HDA_OUTPUT)); - if (err < 0) - return err; - err = add_control(spec, AD_CTL_BIND_MUTE, - "Center Playback Switch", - HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT)); - if (err < 0) - return err; - err = add_control(spec, AD_CTL_BIND_MUTE, - "LFE Playback Switch", - HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT)); - if (err < 0) - return err; - } else { - sprintf(name, "%s Playback Volume", chname[i]); - err = add_control(spec, AD_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = add_control(spec, AD_CTL_BIND_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT)); - if (err < 0) - return err; - } - } - return 0; -} - -/* add playback controls for speaker and HP outputs */ -static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, - const char *pfx) -{ - struct ad198x_spec *spec = codec->spec; - hda_nid_t nid; - int i, idx, err; - char name[32]; - - if (! pin) - return 0; - - idx = ad1988_pin_idx(pin); - nid = ad1988_idx_to_dac(codec, idx); - /* check whether the corresponding DAC was already taken */ - for (i = 0; i < spec->autocfg.line_outs; i++) { - hda_nid_t pin = spec->autocfg.line_out_pins[i]; - hda_nid_t dac = ad1988_idx_to_dac(codec, ad1988_pin_idx(pin)); - if (dac == nid) - break; - } - if (i >= spec->autocfg.line_outs) { - /* specify the DAC as the extra output */ - if (!spec->multiout.hp_nid) - spec->multiout.hp_nid = nid; - else - spec->multiout.extra_out_nid[0] = nid; - /* control HP volume/switch on the output mixer amp */ - sprintf(name, "%s Playback Volume", pfx); - err = add_control(spec, AD_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - } - nid = ad1988_mixer_nids[idx]; - sprintf(name, "%s Playback Switch", pfx); - if ((err = add_control(spec, AD_CTL_BIND_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0) - return err; - return 0; -} - -/* create input playback/capture controls for the given pin */ -static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin, - const char *ctlname, int ctlidx, int boost) -{ - char name[32]; - int err, idx; - - sprintf(name, "%s Playback Volume", ctlname); - idx = ad1988_pin_to_loopback_idx(pin); - if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0) - return err; - sprintf(name, "%s Playback Switch", ctlname); - if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0) - return err; - if (boost) { - hda_nid_t bnid; - idx = ad1988_pin_idx(pin); - bnid = ad1988_boost_nids[idx]; - if (bnid) { - sprintf(name, "%s Boost Volume", ctlname); - return add_control(spec, AD_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT)); - - } - } - return 0; -} - -/* create playback/capture controls for input pins */ -static int ad1988_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - struct ad198x_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux; - int i, err, type, type_idx; - - for (i = 0; i < cfg->num_inputs; i++) { - const char *label; - type = cfg->inputs[i].type; - label = hda_get_autocfg_input_label(codec, cfg, i); - snd_hda_add_imux_item(imux, label, - ad1988_pin_to_adc_idx(cfg->inputs[i].pin), - &type_idx); - err = new_analog_input(spec, cfg->inputs[i].pin, - label, type_idx, - type == AUTO_PIN_MIC); - if (err < 0) - return err; - } - snd_hda_add_imux_item(imux, "Mix", 9, NULL); - - if ((err = add_control(spec, AD_CTL_WIDGET_VOL, - "Analog Mix Playback Volume", - HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0) - return err; - if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, - "Analog Mix Playback Switch", - HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0) - return err; - - return 0; -} - -static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t nid, int pin_type, - int dac_idx) -{ - /* set as output */ - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type); - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - switch (nid) { - case 0x11: /* port-A - DAC 03 */ - snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x00); - break; - case 0x14: /* port-B - DAC 06 */ - snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_CONNECT_SEL, 0x02); - break; - case 0x15: /* port-C - DAC 05 */ - snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_CONNECT_SEL, 0x00); - break; - case 0x17: /* port-E - DAC 0a */ - snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_CONNECT_SEL, 0x01); - break; - case 0x13: /* mono - DAC 04 */ - snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_CONNECT_SEL, 0x01); - break; - } -} - -static void ad1988_auto_init_multi_out(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->autocfg.line_outs; i++) { - hda_nid_t nid = spec->autocfg.line_out_pins[i]; - ad1988_auto_set_output_and_unmute(codec, nid, PIN_OUT, i); - } -} - -static void ad1988_auto_init_extra_out(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - hda_nid_t pin; - - pin = spec->autocfg.speaker_pins[0]; - if (pin) /* connect to front */ - ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); - pin = spec->autocfg.hp_pins[0]; - if (pin) /* connect to front */ - ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); -} - -static void ad1988_auto_init_analog_input(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; - int i, idx; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - int type = cfg->inputs[i].type; - switch (nid) { - case 0x15: /* port-C */ - snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0); - break; - case 0x17: /* port-E */ - snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0); - break; - } - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - type == AUTO_PIN_MIC ? PIN_VREF80 : PIN_IN); - if (nid != AD1988_PIN_CD_NID) - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_MUTE); - idx = ad1988_pin_idx(nid); - if (ad1988_boost_nids[idx]) - snd_hda_codec_write(codec, ad1988_boost_nids[idx], 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_ZERO); - } -} - -/* parse the BIOS configuration and set up the alc_spec */ -/* return 1 if successful, 0 if the proper config is not found, or a negative error code */ -static int ad1988_parse_auto_config(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - int err; - - if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0) - return err; - if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0) - return err; - if (! spec->autocfg.line_outs) - return 0; /* can't find valid BIOS pin config */ - if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 || - (err = ad1988_auto_create_extra_out(codec, - spec->autocfg.speaker_pins[0], - "Speaker")) < 0 || - (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0], - "Headphone")) < 0 || - (err = ad1988_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - if (spec->autocfg.dig_outs) - spec->multiout.dig_out_nid = AD1988_SPDIF_OUT; - if (spec->autocfg.dig_in_pin) - spec->dig_in_nid = AD1988_SPDIF_IN; - - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; - - spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs; - - spec->input_mux = &spec->private_imux; - - return 1; -} - -/* init callback for auto-configuration model -- overriding the default init */ -static int ad1988_auto_init(struct hda_codec *codec) -{ - ad198x_init(codec); - ad1988_auto_init_multi_out(codec); - ad1988_auto_init_extra_out(codec); - ad1988_auto_init_analog_input(codec); - return 0; -} - -/* - */ - -static const char * const ad1988_models[AD1988_MODEL_LAST] = { - [AD1988_6STACK] = "6stack", - [AD1988_6STACK_DIG] = "6stack-dig", - [AD1988_3STACK] = "3stack", - [AD1988_3STACK_DIG] = "3stack-dig", - [AD1988_LAPTOP] = "laptop", - [AD1988_LAPTOP_DIG] = "laptop-dig", - [AD1988_AUTO] = "auto", -}; - -static const struct snd_pci_quirk ad1988_cfg_tbl[] = { - SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG), - SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG), - SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG), - SND_PCI_QUIRK(0x1043, 0x82c0, "Asus M3N-HT Deluxe", AD1988_6STACK_DIG), - SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG), - {} -}; - -static int patch_ad1988(struct hda_codec *codec) -{ - struct ad198x_spec *spec; - int err, board_config; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - - if (is_rev2(codec)) - snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n"); - - board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST, - ad1988_models, ad1988_cfg_tbl); - if (board_config < 0) { - printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - board_config = AD1988_AUTO; - } - - if (board_config == AD1988_AUTO) { - /* automatic parse from the BIOS config */ - err = ad1988_parse_auto_config(codec); - if (err < 0) { - ad198x_free(codec); - return err; - } else if (! err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 6-stack mode...\n"); - board_config = AD1988_6STACK; - } - } - - err = snd_hda_attach_beep_device(codec, 0x10); - if (err < 0) { - ad198x_free(codec); - return err; - } - set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); - - if (!spec->multiout.hp_nid) - spec->multiout.hp_nid = ad1988_alt_dac_nid[0]; - switch (board_config) { - case AD1988_6STACK: - case AD1988_6STACK_DIG: - spec->multiout.max_channels = 8; - spec->multiout.num_dacs = 4; - if (is_rev2(codec)) - spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2; - else - spec->multiout.dac_nids = ad1988_6stack_dac_nids; - spec->input_mux = &ad1988_6stack_capture_source; - spec->num_mixers = 2; - if (is_rev2(codec)) - spec->mixers[0] = ad1988_6stack_mixers1_rev2; - else - spec->mixers[0] = ad1988_6stack_mixers1; - spec->mixers[1] = ad1988_6stack_mixers2; - spec->num_init_verbs = 1; - spec->init_verbs[0] = ad1988_6stack_init_verbs; - if (board_config == AD1988_6STACK_DIG) { - spec->multiout.dig_out_nid = AD1988_SPDIF_OUT; - spec->dig_in_nid = AD1988_SPDIF_IN; - } - break; - case AD1988_3STACK: - case AD1988_3STACK_DIG: - spec->multiout.max_channels = 6; - spec->multiout.num_dacs = 3; - if (is_rev2(codec)) - spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2; - else - spec->multiout.dac_nids = ad1988_3stack_dac_nids; - spec->input_mux = &ad1988_6stack_capture_source; - spec->channel_mode = ad1988_3stack_modes; - spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes); - spec->num_mixers = 2; - if (is_rev2(codec)) - spec->mixers[0] = ad1988_3stack_mixers1_rev2; - else - spec->mixers[0] = ad1988_3stack_mixers1; - spec->mixers[1] = ad1988_3stack_mixers2; - spec->num_init_verbs = 1; - spec->init_verbs[0] = ad1988_3stack_init_verbs; - if (board_config == AD1988_3STACK_DIG) - spec->multiout.dig_out_nid = AD1988_SPDIF_OUT; - break; - case AD1988_LAPTOP: - case AD1988_LAPTOP_DIG: - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = ad1988_3stack_dac_nids; - spec->input_mux = &ad1988_laptop_capture_source; - spec->num_mixers = 1; - spec->mixers[0] = ad1988_laptop_mixers; - spec->inv_eapd = 1; /* inverted EAPD */ - spec->num_init_verbs = 1; - spec->init_verbs[0] = ad1988_laptop_init_verbs; - if (board_config == AD1988_LAPTOP_DIG) - spec->multiout.dig_out_nid = AD1988_SPDIF_OUT; - break; - } - - if (spec->autocfg.hp_pins[0]) { - spec->mixers[spec->num_mixers++] = ad1988_hp_mixers; - spec->slave_vols = ad1988_6stack_fp_slave_pfxs; - spec->slave_sws = ad1988_6stack_fp_slave_pfxs; - spec->alt_dac_nid = ad1988_alt_dac_nid; - spec->stream_analog_alt_playback = - &ad198x_pcm_analog_alt_playback; - } - - spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids); - spec->adc_nids = ad1988_adc_nids; - spec->capsrc_nids = ad1988_capsrc_nids; - spec->mixers[spec->num_mixers++] = ad1988_capture_mixers; - spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs; - if (spec->multiout.dig_out_nid) { - if (codec->vendor_id >= 0x11d4989a) { - spec->mixers[spec->num_mixers++] = - ad1989_spdif_out_mixers; - spec->init_verbs[spec->num_init_verbs++] = - ad1989_spdif_init_verbs; - codec->slave_dig_outs = ad1989b_slave_dig_outs; - } else { - spec->mixers[spec->num_mixers++] = - ad1988_spdif_out_mixers; - spec->init_verbs[spec->num_init_verbs++] = - ad1988_spdif_init_verbs; - } - } - if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a) { - spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers; - spec->init_verbs[spec->num_init_verbs++] = - ad1988_spdif_in_init_verbs; - } - - codec->patch_ops = ad198x_patch_ops; - switch (board_config) { - case AD1988_AUTO: - codec->patch_ops.init = ad1988_auto_init; - break; - case AD1988_LAPTOP: - case AD1988_LAPTOP_DIG: - codec->patch_ops.unsol_event = ad1988_laptop_unsol_event; - break; - } -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = ad1988_loopbacks; -#endif - spec->vmaster_nid = 0x04; - - codec->no_trigger_sense = 1; - codec->no_sticky_stream = 1; - - return 0; -} - - -/* - * AD1884 / AD1984 - * - * port-B - front line/mic-in - * port-E - aux in/out - * port-F - aux in/out - * port-C - rear line/mic-in - * port-D - rear line/hp-out - * port-A - front line/hp-out - * - * AD1984 = AD1884 + two digital mic-ins - * - * FIXME: - * For simplicity, we share the single DAC for both HP and line-outs - * right now. The inidividual playbacks could be easily implemented, - * but no build-up framework is given, so far. - */ - -static const hda_nid_t ad1884_dac_nids[1] = { - 0x04, -}; - -static const hda_nid_t ad1884_adc_nids[2] = { - 0x08, 0x09, -}; - -static const hda_nid_t ad1884_capsrc_nids[2] = { - 0x0c, 0x0d, -}; - -#define AD1884_SPDIF_OUT 0x02 - -static const struct hda_input_mux ad1884_capture_source = { - .num_items = 4, - .items = { - { "Front Mic", 0x0 }, - { "Mic", 0x1 }, - { "CD", 0x2 }, - { "Mix", 0x3 }, - }, -}; - -static const struct snd_kcontrol_new ad1884_base_mixers[] = { - HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT), - /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */ - HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - /* SPDIF controls */ - HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", - /* identical with ad1983 */ - .info = ad1983_spdif_route_info, - .get = ad1983_spdif_route_get, - .put = ad1983_spdif_route_put, - }, - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1984_dmic_mixers[] = { - HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0, - HDA_INPUT), - HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0, - HDA_INPUT), - { } /* end */ -}; - -/* - * initialization verbs - */ -static const struct hda_verb ad1884_init_verbs[] = { - /* DACs; mute as default */ - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* Port-A (HP) mixer */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-A pin */ - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* HP selector - select DAC2 */ - {0x22, AC_VERB_SET_CONNECT_SEL, 0x1}, - /* Port-D (Line-out) mixer */ - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-D pin */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Mono-out mixer */ - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Mono-out pin */ - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Mono selector */ - {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1}, - /* Port-B (front mic) pin */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Port-C (rear mic) pin */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Analog mixer; mute as default */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - /* Analog Mix output amp */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */ - /* SPDIF output selector */ - {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */ - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */ - { } /* end */ -}; - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list ad1884_loopbacks[] = { - { 0x20, HDA_INPUT, 0 }, /* Front Mic */ - { 0x20, HDA_INPUT, 1 }, /* Mic */ - { 0x20, HDA_INPUT, 2 }, /* CD */ - { 0x20, HDA_INPUT, 4 }, /* Docking */ - { } /* end */ -}; -#endif - -static const char * const ad1884_slave_vols[] = { - "PCM", "Mic", "Mono", "Front Mic", "Mic", "CD", - "Internal Mic", "Docking Mic", /* "Beep", */ "IEC958", - NULL -}; - -static int patch_ad1884(struct hda_codec *codec) -{ - struct ad198x_spec *spec; - int err; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - - err = snd_hda_attach_beep_device(codec, 0x10); - if (err < 0) { - ad198x_free(codec); - return err; - } - set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); - - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids); - spec->multiout.dac_nids = ad1884_dac_nids; - spec->multiout.dig_out_nid = AD1884_SPDIF_OUT; - spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids); - spec->adc_nids = ad1884_adc_nids; - spec->capsrc_nids = ad1884_capsrc_nids; - spec->input_mux = &ad1884_capture_source; - spec->num_mixers = 1; - spec->mixers[0] = ad1884_base_mixers; - spec->num_init_verbs = 1; - spec->init_verbs[0] = ad1884_init_verbs; - spec->spdif_route = 0; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = ad1884_loopbacks; -#endif - spec->vmaster_nid = 0x04; - /* we need to cover all playback volumes */ - spec->slave_vols = ad1884_slave_vols; - /* slaves may contain input volumes, so we can't raise to 0dB blindly */ - spec->avoid_init_slave_vol = 1; - - codec->patch_ops = ad198x_patch_ops; - - codec->no_trigger_sense = 1; - codec->no_sticky_stream = 1; - - return 0; -} - -/* - * Lenovo Thinkpad T61/X61 - */ -static const struct hda_input_mux ad1984_thinkpad_capture_source = { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Internal Mic", 0x1 }, - { "Mix", 0x3 }, - { "Docking-Station", 0x4 }, - }, -}; - - -/* - * Dell Precision T3400 - */ -static const struct hda_input_mux ad1984_dell_desktop_capture_source = { - .num_items = 3, - .items = { - { "Front Mic", 0x0 }, - { "Line-In", 0x1 }, - { "Mix", 0x3 }, - }, -}; - - -static const struct snd_kcontrol_new ad1984_thinkpad_mixers[] = { - HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT), - /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */ - HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT), - HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT), - HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - /* SPDIF controls */ - HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", - /* identical with ad1983 */ - .info = ad1983_spdif_route_info, - .get = ad1983_spdif_route_get, - .put = ad1983_spdif_route_put, - }, - { } /* end */ -}; - -/* additional verbs */ -static const struct hda_verb ad1984_thinkpad_init_verbs[] = { - /* Port-E (docking station mic) pin */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* docking mic boost */ - {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* Analog PC Beeper - allow firmware/ACPI beeps */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3) | 0x1a}, - /* Analog mixer - docking mic; mute as default */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* enable EAPD bit */ - {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, - { } /* end */ -}; - -/* - * Dell Precision T3400 - */ -static const struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = { - HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Line-In Boost Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - { } /* end */ -}; - -/* Digial MIC ADC NID 0x05 + 0x06 */ -static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - snd_hda_codec_setup_stream(codec, 0x05 + substream->number, - stream_tag, 0, format); - return 0; -} - -static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number); - return 0; -} - -static const struct hda_pcm_stream ad1984_pcm_dmic_capture = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x05, - .ops = { - .prepare = ad1984_pcm_dmic_prepare, - .cleanup = ad1984_pcm_dmic_cleanup - }, -}; - -static int ad1984_build_pcms(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - struct hda_pcm *info; - int err; - - err = ad198x_build_pcms(codec); - if (err < 0) - return err; - - info = spec->pcm_rec + codec->num_pcms; - codec->num_pcms++; - info->name = "AD1984 Digital Mic"; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture; - return 0; -} - -/* models */ -enum { - AD1984_BASIC, - AD1984_THINKPAD, - AD1984_DELL_DESKTOP, - AD1984_MODELS -}; - -static const char * const ad1984_models[AD1984_MODELS] = { - [AD1984_BASIC] = "basic", - [AD1984_THINKPAD] = "thinkpad", - [AD1984_DELL_DESKTOP] = "dell_desktop", -}; - -static const struct snd_pci_quirk ad1984_cfg_tbl[] = { - /* Lenovo Thinkpad T61/X61 */ - SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD), - SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP), - SND_PCI_QUIRK(0x1028, 0x0233, "Dell Latitude E6400", AD1984_DELL_DESKTOP), - {} -}; - -static int patch_ad1984(struct hda_codec *codec) -{ - struct ad198x_spec *spec; - int board_config, err; - - err = patch_ad1884(codec); - if (err < 0) - return err; - spec = codec->spec; - board_config = snd_hda_check_board_config(codec, AD1984_MODELS, - ad1984_models, ad1984_cfg_tbl); - switch (board_config) { - case AD1984_BASIC: - /* additional digital mics */ - spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers; - codec->patch_ops.build_pcms = ad1984_build_pcms; - break; - case AD1984_THINKPAD: - if (codec->subsystem_id == 0x17aa20fb) { - /* Thinpad X300 does not have the ability to do SPDIF, - or attach to docking station to use SPDIF */ - spec->multiout.dig_out_nid = 0; - } else - spec->multiout.dig_out_nid = AD1884_SPDIF_OUT; - spec->input_mux = &ad1984_thinkpad_capture_source; - spec->mixers[0] = ad1984_thinkpad_mixers; - spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs; - spec->analog_beep = 1; - break; - case AD1984_DELL_DESKTOP: - spec->multiout.dig_out_nid = 0; - spec->input_mux = &ad1984_dell_desktop_capture_source; - spec->mixers[0] = ad1984_dell_desktop_mixers; - break; - } - return 0; -} - - -/* - * AD1883 / AD1884A / AD1984A / AD1984B - * - * port-B (0x14) - front mic-in - * port-E (0x1c) - rear mic-in - * port-F (0x16) - CD / ext out - * port-C (0x15) - rear line-in - * port-D (0x12) - rear line-out - * port-A (0x11) - front hp-out - * - * AD1984A = AD1884A + digital-mic - * AD1883 = equivalent with AD1984A - * AD1984B = AD1984A + extra SPDIF-out - * - * FIXME: - * We share the single DAC for both HP and line-outs (see AD1884/1984). - */ - -static const hda_nid_t ad1884a_dac_nids[1] = { - 0x03, -}; - -#define ad1884a_adc_nids ad1884_adc_nids -#define ad1884a_capsrc_nids ad1884_capsrc_nids - -#define AD1884A_SPDIF_OUT 0x02 - -static const struct hda_input_mux ad1884a_capture_source = { - .num_items = 5, - .items = { - { "Front Mic", 0x0 }, - { "Mic", 0x4 }, - { "Line", 0x1 }, - { "CD", 0x2 }, - { "Mix", 0x3 }, - }, -}; - -static const struct snd_kcontrol_new ad1884a_base_mixers[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Line Boost Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - /* SPDIF controls */ - HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", - /* identical with ad1983 */ - .info = ad1983_spdif_route_info, - .get = ad1983_spdif_route_get, - .put = ad1983_spdif_route_put, - }, - { } /* end */ -}; - -/* - * initialization verbs - */ -static const struct hda_verb ad1884a_init_verbs[] = { - /* DACs; unmute as default */ - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ - /* Port-A (HP) mixer - route only from analog mixer */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-A pin */ - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Port-D (Line-out) mixer - route only from analog mixer */ - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-D pin */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Mono-out mixer - route only from analog mixer */ - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Mono-out pin */ - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Port-B (front mic) pin */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Port-C (rear line-in) pin */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Port-E (rear mic) pin */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */ - /* Port-F (CD) pin */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Analog mixer; mute as default */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, - /* Analog Mix output amp */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* capture sources */ - {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* SPDIF output amp */ - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */ - { } /* end */ -}; - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list ad1884a_loopbacks[] = { - { 0x20, HDA_INPUT, 0 }, /* Front Mic */ - { 0x20, HDA_INPUT, 1 }, /* Mic */ - { 0x20, HDA_INPUT, 2 }, /* CD */ - { 0x20, HDA_INPUT, 4 }, /* Docking */ - { } /* end */ -}; -#endif - -/* - * Laptop model - * - * Port A: Headphone jack - * Port B: MIC jack - * Port C: Internal MIC - * Port D: Dock Line Out (if enabled) - * Port E: Dock Line In (if enabled) - * Port F: Internal speakers - */ - -static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); - int mute = (!ucontrol->value.integer.value[0] && - !ucontrol->value.integer.value[1]); - /* toggle GPIO1 according to the mute state */ - snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - mute ? 0x02 : 0x0); - return ret; -} - -static const struct snd_kcontrol_new ad1884a_laptop_mixers[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_AMP_FLAG, - .info = snd_hda_mixer_amp_switch_info, - .get = snd_hda_mixer_amp_switch_get, - .put = ad1884a_mobile_master_sw_put, - .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), - }, - HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1884a_mobile_mixers[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), - /*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/ - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_AMP_FLAG, - .info = snd_hda_mixer_amp_switch_info, - .get = snd_hda_mixer_amp_switch_get, - .put = ad1884a_mobile_master_sw_put, - .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), - }, - HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -/* mute internal speaker if HP is plugged */ -static void ad1884a_hp_automute(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_jack_detect(codec, 0x11); - snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); - snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE, - present ? 0x00 : 0x02); -} - -/* switch to external mic if plugged */ -static void ad1884a_hp_automic(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_jack_detect(codec, 0x14); - snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, - present ? 0 : 1); -} - -#define AD1884A_HP_EVENT 0x37 -#define AD1884A_MIC_EVENT 0x36 - -/* unsolicited event for HP jack sensing */ -static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res) -{ - switch (res >> 26) { - case AD1884A_HP_EVENT: - ad1884a_hp_automute(codec); - break; - case AD1884A_MIC_EVENT: - ad1884a_hp_automic(codec); - break; - } -} - -/* initialize jack-sensing, too */ -static int ad1884a_hp_init(struct hda_codec *codec) -{ - ad198x_init(codec); - ad1884a_hp_automute(codec); - ad1884a_hp_automic(codec); - return 0; -} - -/* mute internal speaker if HP or docking HP is plugged */ -static void ad1884a_laptop_automute(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_jack_detect(codec, 0x11); - if (!present) - present = snd_hda_jack_detect(codec, 0x12); - snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); - snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE, - present ? 0x00 : 0x02); -} - -/* switch to external mic if plugged */ -static void ad1884a_laptop_automic(struct hda_codec *codec) -{ - unsigned int idx; - - if (snd_hda_jack_detect(codec, 0x14)) - idx = 0; - else if (snd_hda_jack_detect(codec, 0x1c)) - idx = 4; - else - idx = 1; - snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx); -} - -/* unsolicited event for HP jack sensing */ -static void ad1884a_laptop_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - switch (res >> 26) { - case AD1884A_HP_EVENT: - ad1884a_laptop_automute(codec); - break; - case AD1884A_MIC_EVENT: - ad1884a_laptop_automic(codec); - break; - } -} - -/* initialize jack-sensing, too */ -static int ad1884a_laptop_init(struct hda_codec *codec) -{ - ad198x_init(codec); - ad1884a_laptop_automute(codec); - ad1884a_laptop_automic(codec); - return 0; -} - -/* additional verbs for laptop model */ -static const struct hda_verb ad1884a_laptop_verbs[] = { - /* Port-A (HP) pin - always unmuted */ - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Port-F (int speaker) mixer - route only from analog mixer */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-F (int speaker) pin */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* required for compaq 6530s/6531s speaker output */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* Port-C pin - internal mic-in */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */ - /* Port-D (docking line-out) pin - default unmuted */ - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* analog mix */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* unsolicited event for pin-sense */ - {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, - {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT}, - {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT}, - /* allow to touch GPIO1 (for mute control) */ - {0x01, AC_VERB_SET_GPIO_MASK, 0x02}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */ - { } /* end */ -}; - -static const struct hda_verb ad1884a_mobile_verbs[] = { - /* DACs; unmute as default */ - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ - /* Port-A (HP) mixer - route only from analog mixer */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-A pin */ - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* Port-A (HP) pin - always unmuted */ - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Port-B (mic jack) pin */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */ - /* Port-C (int mic) pin */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */ - /* Port-F (int speaker) mixer - route only from analog mixer */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-F pin */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Analog mixer; mute as default */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, - /* Analog Mix output amp */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* capture sources */ - /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* unsolicited event for pin-sense */ - {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT}, - /* allow to touch GPIO1 (for mute control) */ - {0x01, AC_VERB_SET_GPIO_MASK, 0x02}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */ - { } /* end */ -}; - -/* - * Thinkpad X300 - * 0x11 - HP - * 0x12 - speaker - * 0x14 - mic-in - * 0x17 - built-in mic - */ - -static const struct hda_verb ad1984a_thinkpad_verbs[] = { - /* HP unmute */ - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* analog mix */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* turn on EAPD */ - {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, - /* unsolicited event for pin-sense */ - {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, - /* internal mic - dmic */ - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - /* set magic COEFs for dmic */ - {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7}, - {0x01, AC_VERB_SET_PROC_COEF, 0x08}, - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x17, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - { } /* end */ -}; - -static const struct hda_input_mux ad1984a_thinkpad_capture_source = { - .num_items = 3, - .items = { - { "Mic", 0x0 }, - { "Internal Mic", 0x5 }, - { "Mix", 0x3 }, - }, -}; - -/* mute internal speaker if HP is plugged */ -static void ad1984a_thinkpad_automute(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_jack_detect(codec, 0x11); - snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); -} - -/* unsolicited event for HP jack sensing */ -static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - if ((res >> 26) != AD1884A_HP_EVENT) - return; - ad1984a_thinkpad_automute(codec); -} - -/* initialize jack-sensing, too */ -static int ad1984a_thinkpad_init(struct hda_codec *codec) -{ - ad198x_init(codec); - ad1984a_thinkpad_automute(codec); - return 0; -} - -/* - * Precision R5500 - * 0x12 - HP/line-out - * 0x13 - speaker (mono) - * 0x15 - mic-in - */ - -static const struct hda_verb ad1984a_precision_verbs[] = { - /* Unmute main output path */ - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x1f}, /* 0dB */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5) + 0x17}, /* 0dB */ - /* Analog mixer; mute as default */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* Select mic as input */ - {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x27}, /* 0dB */ - /* Configure as mic */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */ - /* HP unmute */ - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* turn on EAPD */ - {0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, - /* unsolicited event for pin-sense */ - {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1984a_precision_mixers[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - { } /* end */ -}; - - -/* mute internal speaker if HP is plugged */ -static void ad1984a_precision_automute(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_jack_detect(codec, 0x12); - snd_hda_codec_amp_stereo(codec, 0x13, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); -} - - -/* unsolicited event for HP jack sensing */ -static void ad1984a_precision_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - if ((res >> 26) != AD1884A_HP_EVENT) - return; - ad1984a_precision_automute(codec); -} - -/* initialize jack-sensing, too */ -static int ad1984a_precision_init(struct hda_codec *codec) -{ - ad198x_init(codec); - ad1984a_precision_automute(codec); - return 0; -} - - -/* - * HP Touchsmart - * port-A (0x11) - front hp-out - * port-B (0x14) - unused - * port-C (0x15) - unused - * port-D (0x12) - rear line out - * port-E (0x1c) - front mic-in - * port-F (0x16) - Internal speakers - * digital-mic (0x17) - Internal mic - */ - -static const struct hda_verb ad1984a_touchsmart_verbs[] = { - /* DACs; unmute as default */ - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ - /* Port-A (HP) mixer - route only from analog mixer */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-A pin */ - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* Port-A (HP) pin - always unmuted */ - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Port-E (int speaker) mixer - route only from analog mixer */ - {0x25, AC_VERB_SET_AMP_GAIN_MUTE, 0x03}, - /* Port-E pin */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - /* Port-F (int speaker) mixer - route only from analog mixer */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-F pin */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Analog mixer; mute as default */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, - /* Analog Mix output amp */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* capture sources */ - /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* unsolicited event for pin-sense */ - {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, - {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT}, - /* allow to touch GPIO1 (for mute control) */ - {0x01, AC_VERB_SET_GPIO_MASK, 0x02}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */ - /* internal mic - dmic */ - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - /* set magic COEFs for dmic */ - {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7}, - {0x01, AC_VERB_SET_PROC_COEF, 0x08}, - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), -/* HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/ - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .subdevice = HDA_SUBDEV_AMP_FLAG, - .name = "Master Playback Switch", - .info = snd_hda_mixer_amp_switch_info, - .get = snd_hda_mixer_amp_switch_get, - .put = ad1884a_mobile_master_sw_put, - .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), - }, - HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x17, 0x0, HDA_INPUT), - { } /* end */ -}; - -/* switch to external mic if plugged */ -static void ad1984a_touchsmart_automic(struct hda_codec *codec) -{ - if (snd_hda_jack_detect(codec, 0x1c)) - snd_hda_codec_write(codec, 0x0c, 0, - AC_VERB_SET_CONNECT_SEL, 0x4); - else - snd_hda_codec_write(codec, 0x0c, 0, - AC_VERB_SET_CONNECT_SEL, 0x5); -} - - -/* unsolicited event for HP jack sensing */ -static void ad1984a_touchsmart_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - switch (res >> 26) { - case AD1884A_HP_EVENT: - ad1884a_hp_automute(codec); - break; - case AD1884A_MIC_EVENT: - ad1984a_touchsmart_automic(codec); - break; - } -} - -/* initialize jack-sensing, too */ -static int ad1984a_touchsmart_init(struct hda_codec *codec) -{ - ad198x_init(codec); - ad1884a_hp_automute(codec); - ad1984a_touchsmart_automic(codec); - return 0; -} - - -/* - */ - -enum { - AD1884A_DESKTOP, - AD1884A_LAPTOP, - AD1884A_MOBILE, - AD1884A_THINKPAD, - AD1984A_TOUCHSMART, - AD1984A_PRECISION, - AD1884A_MODELS -}; - -static const char * const ad1884a_models[AD1884A_MODELS] = { - [AD1884A_DESKTOP] = "desktop", - [AD1884A_LAPTOP] = "laptop", - [AD1884A_MOBILE] = "mobile", - [AD1884A_THINKPAD] = "thinkpad", - [AD1984A_TOUCHSMART] = "touchsmart", - [AD1984A_PRECISION] = "precision", -}; - -static const struct snd_pci_quirk ad1884a_cfg_tbl[] = { - SND_PCI_QUIRK(0x1028, 0x04ac, "Precision R5500", AD1984A_PRECISION), - SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE), - SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP), - SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE), - SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE), - SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP), - SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP), - SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP), - SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE), - SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD), - SND_PCI_QUIRK(0x103c, 0x2a82, "Touchsmart", AD1984A_TOUCHSMART), - {} -}; - -static int patch_ad1884a(struct hda_codec *codec) -{ - struct ad198x_spec *spec; - int err, board_config; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - - err = snd_hda_attach_beep_device(codec, 0x10); - if (err < 0) { - ad198x_free(codec); - return err; - } - set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); - - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids); - spec->multiout.dac_nids = ad1884a_dac_nids; - spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT; - spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids); - spec->adc_nids = ad1884a_adc_nids; - spec->capsrc_nids = ad1884a_capsrc_nids; - spec->input_mux = &ad1884a_capture_source; - spec->num_mixers = 1; - spec->mixers[0] = ad1884a_base_mixers; - spec->num_init_verbs = 1; - spec->init_verbs[0] = ad1884a_init_verbs; - spec->spdif_route = 0; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = ad1884a_loopbacks; -#endif - codec->patch_ops = ad198x_patch_ops; - - /* override some parameters */ - board_config = snd_hda_check_board_config(codec, AD1884A_MODELS, - ad1884a_models, - ad1884a_cfg_tbl); - switch (board_config) { - case AD1884A_LAPTOP: - spec->mixers[0] = ad1884a_laptop_mixers; - spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs; - spec->multiout.dig_out_nid = 0; - codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event; - codec->patch_ops.init = ad1884a_laptop_init; - /* set the upper-limit for mixer amp to 0dB for avoiding the - * possible damage by overloading - */ - snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT, - (0x17 << AC_AMPCAP_OFFSET_SHIFT) | - (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (1 << AC_AMPCAP_MUTE_SHIFT)); - break; - case AD1884A_MOBILE: - spec->mixers[0] = ad1884a_mobile_mixers; - spec->init_verbs[0] = ad1884a_mobile_verbs; - spec->multiout.dig_out_nid = 0; - codec->patch_ops.unsol_event = ad1884a_hp_unsol_event; - codec->patch_ops.init = ad1884a_hp_init; - /* set the upper-limit for mixer amp to 0dB for avoiding the - * possible damage by overloading - */ - snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT, - (0x17 << AC_AMPCAP_OFFSET_SHIFT) | - (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (1 << AC_AMPCAP_MUTE_SHIFT)); - break; - case AD1884A_THINKPAD: - spec->mixers[0] = ad1984a_thinkpad_mixers; - spec->init_verbs[spec->num_init_verbs++] = - ad1984a_thinkpad_verbs; - spec->multiout.dig_out_nid = 0; - spec->input_mux = &ad1984a_thinkpad_capture_source; - codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event; - codec->patch_ops.init = ad1984a_thinkpad_init; - break; - case AD1984A_PRECISION: - spec->mixers[0] = ad1984a_precision_mixers; - spec->init_verbs[spec->num_init_verbs++] = - ad1984a_precision_verbs; - spec->multiout.dig_out_nid = 0; - codec->patch_ops.unsol_event = ad1984a_precision_unsol_event; - codec->patch_ops.init = ad1984a_precision_init; - break; - case AD1984A_TOUCHSMART: - spec->mixers[0] = ad1984a_touchsmart_mixers; - spec->init_verbs[0] = ad1984a_touchsmart_verbs; - spec->multiout.dig_out_nid = 0; - codec->patch_ops.unsol_event = ad1984a_touchsmart_unsol_event; - codec->patch_ops.init = ad1984a_touchsmart_init; - /* set the upper-limit for mixer amp to 0dB for avoiding the - * possible damage by overloading - */ - snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT, - (0x17 << AC_AMPCAP_OFFSET_SHIFT) | - (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (1 << AC_AMPCAP_MUTE_SHIFT)); - break; - } - - codec->no_trigger_sense = 1; - codec->no_sticky_stream = 1; - - return 0; -} - - -/* - * AD1882 / AD1882A - * - * port-A - front hp-out - * port-B - front mic-in - * port-C - rear line-in, shared surr-out (3stack) - * port-D - rear line-out - * port-E - rear mic-in, shared clfe-out (3stack) - * port-F - rear surr-out (6stack) - * port-G - rear clfe-out (6stack) - */ - -static const hda_nid_t ad1882_dac_nids[3] = { - 0x04, 0x03, 0x05 -}; - -static const hda_nid_t ad1882_adc_nids[2] = { - 0x08, 0x09, -}; - -static const hda_nid_t ad1882_capsrc_nids[2] = { - 0x0c, 0x0d, -}; - -#define AD1882_SPDIF_OUT 0x02 - -/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */ -static const struct hda_input_mux ad1882_capture_source = { - .num_items = 5, - .items = { - { "Front Mic", 0x1 }, - { "Mic", 0x4 }, - { "Line", 0x2 }, - { "CD", 0x3 }, - { "Mix", 0x7 }, - }, -}; - -/* list: 0x11, 0x39, 0x3a, 0x3c, 0x18, 0x1f, 0x12, 0x20 */ -static const struct hda_input_mux ad1882a_capture_source = { - .num_items = 5, - .items = { - { "Front Mic", 0x1 }, - { "Mic", 0x4}, - { "Line", 0x2 }, - { "Digital Mic", 0x06 }, - { "Mix", 0x7 }, - }, -}; - -static const struct snd_kcontrol_new ad1882_base_mixers[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT), - - HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Line-In Boost Volume", 0x3a, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - /* SPDIF controls */ - HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", - /* identical with ad1983 */ - .info = ad1983_spdif_route_info, - .get = ad1983_spdif_route_get, - .put = ad1983_spdif_route_put, - }, - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1882_loopback_mixers[] = { - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1882a_loopback_mixers[] = { - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT), - HDA_CODEC_VOLUME("Digital Mic Boost Volume", 0x1f, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1882_3stack_mixers[] = { - HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = ad198x_ch_mode_info, - .get = ad198x_ch_mode_get, - .put = ad198x_ch_mode_put, - }, - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1882_6stack_mixers[] = { - HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct hda_verb ad1882_ch2_init[] = { - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - { } /* end */ -}; - -static const struct hda_verb ad1882_ch4_init[] = { - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - { } /* end */ -}; - -static const struct hda_verb ad1882_ch6_init[] = { - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - { } /* end */ -}; - -static const struct hda_channel_mode ad1882_modes[3] = { - { 2, ad1882_ch2_init }, - { 4, ad1882_ch4_init }, - { 6, ad1882_ch6_init }, -}; - -/* - * initialization verbs - */ -static const struct hda_verb ad1882_init_verbs[] = { - /* DACs; mute as default */ - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* Port-A (HP) mixer */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-A pin */ - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* HP selector - select DAC2 */ - {0x37, AC_VERB_SET_CONNECT_SEL, 0x1}, - /* Port-D (Line-out) mixer */ - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-D pin */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Mono-out mixer */ - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Mono-out pin */ - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Port-B (front mic) pin */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */ - /* Port-C (line-in) pin */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */ - /* Port-C mixer - mute as input */ - {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* Port-E (mic-in) pin */ - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */ - /* Port-E mixer - mute as input */ - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* Port-F (surround) */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Port-G (CLFE) */ - {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Analog mixer; mute as default */ - /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, - /* Analog Mix output amp */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */ - /* SPDIF output selector */ - {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */ - {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */ - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */ - { } /* end */ -}; - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list ad1882_loopbacks[] = { - { 0x20, HDA_INPUT, 0 }, /* Front Mic */ - { 0x20, HDA_INPUT, 1 }, /* Mic */ - { 0x20, HDA_INPUT, 4 }, /* Line */ - { 0x20, HDA_INPUT, 6 }, /* CD */ - { } /* end */ -}; -#endif - -/* models */ -enum { - AD1882_3STACK, - AD1882_6STACK, - AD1882_MODELS -}; - -static const char * const ad1882_models[AD1986A_MODELS] = { - [AD1882_3STACK] = "3stack", - [AD1882_6STACK] = "6stack", -}; - - -static int patch_ad1882(struct hda_codec *codec) -{ - struct ad198x_spec *spec; - int err, board_config; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - - err = snd_hda_attach_beep_device(codec, 0x10); - if (err < 0) { - ad198x_free(codec); - return err; - } - set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); - - spec->multiout.max_channels = 6; - spec->multiout.num_dacs = 3; - spec->multiout.dac_nids = ad1882_dac_nids; - spec->multiout.dig_out_nid = AD1882_SPDIF_OUT; - spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids); - spec->adc_nids = ad1882_adc_nids; - spec->capsrc_nids = ad1882_capsrc_nids; - if (codec->vendor_id == 0x11d41882) - spec->input_mux = &ad1882_capture_source; - else - spec->input_mux = &ad1882a_capture_source; - spec->num_mixers = 2; - spec->mixers[0] = ad1882_base_mixers; - if (codec->vendor_id == 0x11d41882) - spec->mixers[1] = ad1882_loopback_mixers; - else - spec->mixers[1] = ad1882a_loopback_mixers; - spec->num_init_verbs = 1; - spec->init_verbs[0] = ad1882_init_verbs; - spec->spdif_route = 0; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = ad1882_loopbacks; -#endif - spec->vmaster_nid = 0x04; - - codec->patch_ops = ad198x_patch_ops; - - /* override some parameters */ - board_config = snd_hda_check_board_config(codec, AD1882_MODELS, - ad1882_models, NULL); - switch (board_config) { - default: - case AD1882_3STACK: - spec->num_mixers = 3; - spec->mixers[2] = ad1882_3stack_mixers; - spec->channel_mode = ad1882_modes; - spec->num_channel_mode = ARRAY_SIZE(ad1882_modes); - spec->need_dac_fix = 1; - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = 1; - break; - case AD1882_6STACK: - spec->num_mixers = 3; - spec->mixers[2] = ad1882_6stack_mixers; - break; - } - - codec->no_trigger_sense = 1; - codec->no_sticky_stream = 1; - - return 0; -} - - -/* - * patch entries - */ -static const struct hda_codec_preset snd_hda_preset_analog[] = { - { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a }, - { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 }, - { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a }, - { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 }, - { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a }, - { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a }, - { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 }, - { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 }, - { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 }, - { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a }, - { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 }, - { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 }, - { .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 }, - { .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 }, - { .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/ANDROID_3.4.5/sound/pci/hda/patch_ca0110.c b/ANDROID_3.4.5/sound/pci/hda/patch_ca0110.c deleted file mode 100644 index 09ccfabb..00000000 --- a/ANDROID_3.4.5/sound/pci/hda/patch_ca0110.c +++ /dev/null @@ -1,576 +0,0 @@ -/* - * HD audio interface patch for Creative X-Fi CA0110-IBG chip - * - * Copyright (c) 2008 Takashi Iwai <tiwai@suse.de> - * - * 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 <linux/delay.h> -#include <linux/slab.h> -#include <linux/pci.h> -#include <linux/module.h> -#include <sound/core.h> -#include "hda_codec.h" -#include "hda_local.h" - -/* - */ - -struct ca0110_spec { - struct auto_pin_cfg autocfg; - struct hda_multi_out multiout; - hda_nid_t out_pins[AUTO_CFG_MAX_OUTS]; - hda_nid_t dacs[AUTO_CFG_MAX_OUTS]; - hda_nid_t hp_dac; - hda_nid_t input_pins[AUTO_PIN_LAST]; - hda_nid_t adcs[AUTO_PIN_LAST]; - hda_nid_t dig_out; - hda_nid_t dig_in; - unsigned int num_inputs; - char input_labels[AUTO_PIN_LAST][32]; - struct hda_pcm pcm_rec[2]; /* PCM information */ -}; - -/* - * PCM callbacks - */ -static int ca0110_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ca0110_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, - hinfo); -} - -static int ca0110_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 ca0110_spec *spec = codec->spec; - return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, - stream_tag, format, substream); -} - -static int ca0110_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ca0110_spec *spec = codec->spec; - return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); -} - -/* - * Digital out - */ -static int ca0110_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ca0110_spec *spec = codec->spec; - return snd_hda_multi_out_dig_open(codec, &spec->multiout); -} - -static int ca0110_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ca0110_spec *spec = codec->spec; - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} - -static int ca0110_dig_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 ca0110_spec *spec = codec->spec; - return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, - format, substream); -} - -/* - * Analog capture - */ -static int ca0110_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct ca0110_spec *spec = codec->spec; - - snd_hda_codec_setup_stream(codec, spec->adcs[substream->number], - stream_tag, 0, format); - return 0; -} - -static int ca0110_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ca0110_spec *spec = codec->spec; - - snd_hda_codec_cleanup_stream(codec, spec->adcs[substream->number]); - return 0; -} - -/* - */ - -static const char * const dirstr[2] = { "Playback", "Capture" }; - -static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx, - int chan, int dir) -{ - char namestr[44]; - int type = dir ? HDA_INPUT : HDA_OUTPUT; - struct snd_kcontrol_new knew = - HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type); - sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]); - return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); -} - -static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx, - int chan, int dir) -{ - char namestr[44]; - int type = dir ? HDA_INPUT : HDA_OUTPUT; - struct snd_kcontrol_new knew = - HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type); - sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]); - return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); -} - -#define add_out_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 0) -#define add_out_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 0) -#define add_in_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 1) -#define add_in_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 1) -#define add_mono_switch(codec, nid, pfx, chan) \ - _add_switch(codec, nid, pfx, chan, 0) -#define add_mono_volume(codec, nid, pfx, chan) \ - _add_volume(codec, nid, pfx, chan, 0) - -static int ca0110_build_controls(struct hda_codec *codec) -{ - struct ca0110_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - static const char * const prefix[AUTO_CFG_MAX_OUTS] = { - "Front", "Surround", NULL, "Side", "Multi" - }; - hda_nid_t mutenid; - int i, err; - - for (i = 0; i < spec->multiout.num_dacs; i++) { - if (get_wcaps(codec, spec->out_pins[i]) & AC_WCAP_OUT_AMP) - mutenid = spec->out_pins[i]; - else - mutenid = spec->multiout.dac_nids[i]; - if (!prefix[i]) { - err = add_mono_switch(codec, mutenid, - "Center", 1); - if (err < 0) - return err; - err = add_mono_switch(codec, mutenid, - "LFE", 1); - if (err < 0) - return err; - err = add_mono_volume(codec, spec->multiout.dac_nids[i], - "Center", 1); - if (err < 0) - return err; - err = add_mono_volume(codec, spec->multiout.dac_nids[i], - "LFE", 1); - if (err < 0) - return err; - } else { - err = add_out_switch(codec, mutenid, - prefix[i]); - if (err < 0) - return err; - err = add_out_volume(codec, spec->multiout.dac_nids[i], - prefix[i]); - if (err < 0) - return err; - } - } - if (cfg->hp_outs) { - if (get_wcaps(codec, cfg->hp_pins[0]) & AC_WCAP_OUT_AMP) - mutenid = cfg->hp_pins[0]; - else - mutenid = spec->multiout.dac_nids[i]; - - err = add_out_switch(codec, mutenid, "Headphone"); - if (err < 0) - return err; - if (spec->hp_dac) { - err = add_out_volume(codec, spec->hp_dac, "Headphone"); - if (err < 0) - return err; - } - } - for (i = 0; i < spec->num_inputs; i++) { - const char *label = spec->input_labels[i]; - if (get_wcaps(codec, spec->input_pins[i]) & AC_WCAP_IN_AMP) - mutenid = spec->input_pins[i]; - else - mutenid = spec->adcs[i]; - err = add_in_switch(codec, mutenid, label); - if (err < 0) - return err; - err = add_in_volume(codec, spec->adcs[i], label); - if (err < 0) - return err; - } - - if (spec->dig_out) { - err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out, - spec->dig_out); - if (err < 0) - return err; - err = snd_hda_create_spdif_share_sw(codec, &spec->multiout); - if (err < 0) - return err; - spec->multiout.share_spdif = 1; - } - if (spec->dig_in) { - err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in); - if (err < 0) - return err; - err = add_in_volume(codec, spec->dig_in, "IEC958"); - } - return 0; -} - -/* - */ -static const struct hda_pcm_stream ca0110_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 8, - .ops = { - .open = ca0110_playback_pcm_open, - .prepare = ca0110_playback_pcm_prepare, - .cleanup = ca0110_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream ca0110_pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .ops = { - .prepare = ca0110_capture_pcm_prepare, - .cleanup = ca0110_capture_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream ca0110_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .ops = { - .open = ca0110_dig_playback_pcm_open, - .close = ca0110_dig_playback_pcm_close, - .prepare = ca0110_dig_playback_pcm_prepare - }, -}; - -static const struct hda_pcm_stream ca0110_pcm_digital_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, -}; - -static int ca0110_build_pcms(struct hda_codec *codec) -{ - struct ca0110_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; - - codec->pcm_info = info; - codec->num_pcms = 0; - - info->name = "CA0110 Analog"; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0110_pcm_analog_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0]; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = - spec->multiout.max_channels; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0110_pcm_analog_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_inputs; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0]; - codec->num_pcms++; - - if (!spec->dig_out && !spec->dig_in) - return 0; - - info++; - info->name = "CA0110 Digital"; - info->pcm_type = HDA_PCM_TYPE_SPDIF; - if (spec->dig_out) { - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = - ca0110_pcm_digital_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out; - } - if (spec->dig_in) { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = - ca0110_pcm_digital_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in; - } - codec->num_pcms++; - - return 0; -} - -static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) -{ - if (pin) { - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP); - if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP) - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); - } - if (dac) - snd_hda_codec_write(codec, dac, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO); -} - -static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc) -{ - if (pin) { - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80); - if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP) - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(0)); - } - if (adc) - snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(0)); -} - -static int ca0110_init(struct hda_codec *codec) -{ - struct ca0110_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - for (i = 0; i < spec->multiout.num_dacs; i++) - init_output(codec, spec->out_pins[i], - spec->multiout.dac_nids[i]); - init_output(codec, cfg->hp_pins[0], spec->hp_dac); - init_output(codec, cfg->dig_out_pins[0], spec->dig_out); - - for (i = 0; i < spec->num_inputs; i++) - init_input(codec, spec->input_pins[i], spec->adcs[i]); - init_input(codec, cfg->dig_in_pin, spec->dig_in); - return 0; -} - -static void ca0110_free(struct hda_codec *codec) -{ - kfree(codec->spec); -} - -static const struct hda_codec_ops ca0110_patch_ops = { - .build_controls = ca0110_build_controls, - .build_pcms = ca0110_build_pcms, - .init = ca0110_init, - .free = ca0110_free, -}; - - -static void parse_line_outs(struct hda_codec *codec) -{ - struct ca0110_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i, n; - unsigned int def_conf; - hda_nid_t nid; - - n = 0; - for (i = 0; i < cfg->line_outs; i++) { - nid = cfg->line_out_pins[i]; - def_conf = snd_hda_codec_get_pincfg(codec, nid); - if (!def_conf) - continue; /* invalid pin */ - if (snd_hda_get_connections(codec, nid, &spec->dacs[i], 1) != 1) - continue; - spec->out_pins[n++] = nid; - } - spec->multiout.dac_nids = spec->dacs; - spec->multiout.num_dacs = n; - spec->multiout.max_channels = n * 2; -} - -static void parse_hp_out(struct hda_codec *codec) -{ - struct ca0110_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - unsigned int def_conf; - hda_nid_t nid, dac; - - if (!cfg->hp_outs) - return; - nid = cfg->hp_pins[0]; - def_conf = snd_hda_codec_get_pincfg(codec, nid); - if (!def_conf) { - cfg->hp_outs = 0; - return; - } - if (snd_hda_get_connections(codec, nid, &dac, 1) != 1) - return; - - for (i = 0; i < cfg->line_outs; i++) - if (dac == spec->dacs[i]) - break; - if (i >= cfg->line_outs) { - spec->hp_dac = dac; - spec->multiout.hp_nid = dac; - } -} - -static void parse_input(struct hda_codec *codec) -{ - struct ca0110_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t nid, pin; - int n, i, j; - - n = 0; - nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, nid++) { - unsigned int wcaps = get_wcaps(codec, nid); - unsigned int type = get_wcaps_type(wcaps); - if (type != AC_WID_AUD_IN) - continue; - if (snd_hda_get_connections(codec, nid, &pin, 1) != 1) - continue; - if (pin == cfg->dig_in_pin) { - spec->dig_in = nid; - continue; - } - for (j = 0; j < cfg->num_inputs; j++) - if (cfg->inputs[j].pin == pin) - break; - if (j >= cfg->num_inputs) - continue; - spec->input_pins[n] = pin; - snd_hda_get_pin_label(codec, pin, cfg, - spec->input_labels[n], - sizeof(spec->input_labels[n]), NULL); - spec->adcs[n] = nid; - n++; - } - spec->num_inputs = n; -} - -static void parse_digital(struct hda_codec *codec) -{ - struct ca0110_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - - if (cfg->dig_outs && - snd_hda_get_connections(codec, cfg->dig_out_pins[0], - &spec->dig_out, 1) == 1) - spec->multiout.dig_out_nid = spec->dig_out; -} - -static int ca0110_parse_auto_config(struct hda_codec *codec) -{ - struct ca0110_spec *spec = codec->spec; - int err; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - - parse_line_outs(codec); - parse_hp_out(codec); - parse_digital(codec); - parse_input(codec); - return 0; -} - - -static int patch_ca0110(struct hda_codec *codec) -{ - struct ca0110_spec *spec; - int err; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return -ENOMEM; - codec->spec = spec; - - codec->bus->needs_damn_long_delay = 1; - - err = ca0110_parse_auto_config(codec); - if (err < 0) - goto error; - - codec->patch_ops = ca0110_patch_ops; - - return 0; - - error: - kfree(codec->spec); - codec->spec = NULL; - return err; -} - - -/* - * patch entries - */ -static const struct hda_codec_preset snd_hda_preset_ca0110[] = { - { .id = 0x1102000a, .name = "CA0110-IBG", .patch = patch_ca0110 }, - { .id = 0x1102000b, .name = "CA0110-IBG", .patch = patch_ca0110 }, - { .id = 0x1102000d, .name = "SB0880 X-Fi", .patch = patch_ca0110 }, - {} /* terminator */ -}; - -MODULE_ALIAS("snd-hda-codec-id:1102000a"); -MODULE_ALIAS("snd-hda-codec-id:1102000b"); -MODULE_ALIAS("snd-hda-codec-id:1102000d"); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Creative CA0110-IBG HD-audio codec"); - -static struct hda_codec_preset_list ca0110_list = { - .preset = snd_hda_preset_ca0110, - .owner = THIS_MODULE, -}; - -static int __init patch_ca0110_init(void) -{ - return snd_hda_add_codec_preset(&ca0110_list); -} - -static void __exit patch_ca0110_exit(void) -{ - snd_hda_delete_codec_preset(&ca0110_list); -} - -module_init(patch_ca0110_init) -module_exit(patch_ca0110_exit) diff --git a/ANDROID_3.4.5/sound/pci/hda/patch_ca0132.c b/ANDROID_3.4.5/sound/pci/hda/patch_ca0132.c deleted file mode 100644 index 21d91d58..00000000 --- a/ANDROID_3.4.5/sound/pci/hda/patch_ca0132.c +++ /dev/null @@ -1,1103 +0,0 @@ -/* - * HD audio interface patch for Creative CA0132 chip - * - * Copyright (c) 2011, Creative Technology Ltd. - * - * Based on patch_ca0110.c - * Copyright (c) 2008 Takashi Iwai <tiwai@suse.de> - * - * 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 <linux/delay.h> -#include <linux/slab.h> -#include <linux/pci.h> -#include <linux/mutex.h> -#include <linux/module.h> -#include <sound/core.h> -#include "hda_codec.h" -#include "hda_local.h" - -#define WIDGET_CHIP_CTRL 0x15 -#define WIDGET_DSP_CTRL 0x16 - -#define WUH_MEM_CONNID 10 -#define DSP_MEM_CONNID 16 - -enum hda_cmd_vendor_io { - /* for DspIO node */ - VENDOR_DSPIO_SCP_WRITE_DATA_LOW = 0x000, - VENDOR_DSPIO_SCP_WRITE_DATA_HIGH = 0x100, - - VENDOR_DSPIO_STATUS = 0xF01, - VENDOR_DSPIO_SCP_POST_READ_DATA = 0x702, - VENDOR_DSPIO_SCP_READ_DATA = 0xF02, - VENDOR_DSPIO_DSP_INIT = 0x703, - VENDOR_DSPIO_SCP_POST_COUNT_QUERY = 0x704, - VENDOR_DSPIO_SCP_READ_COUNT = 0xF04, - - /* for ChipIO node */ - VENDOR_CHIPIO_ADDRESS_LOW = 0x000, - VENDOR_CHIPIO_ADDRESS_HIGH = 0x100, - VENDOR_CHIPIO_STREAM_FORMAT = 0x200, - VENDOR_CHIPIO_DATA_LOW = 0x300, - VENDOR_CHIPIO_DATA_HIGH = 0x400, - - VENDOR_CHIPIO_GET_PARAMETER = 0xF00, - VENDOR_CHIPIO_STATUS = 0xF01, - VENDOR_CHIPIO_HIC_POST_READ = 0x702, - VENDOR_CHIPIO_HIC_READ_DATA = 0xF03, - - VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE = 0x70A, - - VENDOR_CHIPIO_PLL_PMU_WRITE = 0x70C, - VENDOR_CHIPIO_PLL_PMU_READ = 0xF0C, - VENDOR_CHIPIO_8051_ADDRESS_LOW = 0x70D, - VENDOR_CHIPIO_8051_ADDRESS_HIGH = 0x70E, - VENDOR_CHIPIO_FLAG_SET = 0x70F, - VENDOR_CHIPIO_FLAGS_GET = 0xF0F, - VENDOR_CHIPIO_PARAMETER_SET = 0x710, - VENDOR_CHIPIO_PARAMETER_GET = 0xF10, - - VENDOR_CHIPIO_PORT_ALLOC_CONFIG_SET = 0x711, - VENDOR_CHIPIO_PORT_ALLOC_SET = 0x712, - VENDOR_CHIPIO_PORT_ALLOC_GET = 0xF12, - VENDOR_CHIPIO_PORT_FREE_SET = 0x713, - - VENDOR_CHIPIO_PARAMETER_EX_ID_GET = 0xF17, - VENDOR_CHIPIO_PARAMETER_EX_ID_SET = 0x717, - VENDOR_CHIPIO_PARAMETER_EX_VALUE_GET = 0xF18, - VENDOR_CHIPIO_PARAMETER_EX_VALUE_SET = 0x718 -}; - -/* - * Control flag IDs - */ -enum control_flag_id { - /* Connection manager stream setup is bypassed/enabled */ - CONTROL_FLAG_C_MGR = 0, - /* DSP DMA is bypassed/enabled */ - CONTROL_FLAG_DMA = 1, - /* 8051 'idle' mode is disabled/enabled */ - CONTROL_FLAG_IDLE_ENABLE = 2, - /* Tracker for the SPDIF-in path is bypassed/enabled */ - CONTROL_FLAG_TRACKER = 3, - /* DigitalOut to Spdif2Out connection is disabled/enabled */ - CONTROL_FLAG_SPDIF2OUT = 4, - /* Digital Microphone is disabled/enabled */ - CONTROL_FLAG_DMIC = 5, - /* ADC_B rate is 48 kHz/96 kHz */ - CONTROL_FLAG_ADC_B_96KHZ = 6, - /* ADC_C rate is 48 kHz/96 kHz */ - CONTROL_FLAG_ADC_C_96KHZ = 7, - /* DAC rate is 48 kHz/96 kHz (affects all DACs) */ - CONTROL_FLAG_DAC_96KHZ = 8, - /* DSP rate is 48 kHz/96 kHz */ - CONTROL_FLAG_DSP_96KHZ = 9, - /* SRC clock is 98 MHz/196 MHz (196 MHz forces rate to 96 KHz) */ - CONTROL_FLAG_SRC_CLOCK_196MHZ = 10, - /* SRC rate is 48 kHz/96 kHz (48 kHz disabled when clock is 196 MHz) */ - CONTROL_FLAG_SRC_RATE_96KHZ = 11, - /* Decode Loop (DSP->SRC->DSP) is disabled/enabled */ - CONTROL_FLAG_DECODE_LOOP = 12, - /* De-emphasis filter on DAC-1 disabled/enabled */ - CONTROL_FLAG_DAC1_DEEMPHASIS = 13, - /* De-emphasis filter on DAC-2 disabled/enabled */ - CONTROL_FLAG_DAC2_DEEMPHASIS = 14, - /* De-emphasis filter on DAC-3 disabled/enabled */ - CONTROL_FLAG_DAC3_DEEMPHASIS = 15, - /* High-pass filter on ADC_B disabled/enabled */ - CONTROL_FLAG_ADC_B_HIGH_PASS = 16, - /* High-pass filter on ADC_C disabled/enabled */ - CONTROL_FLAG_ADC_C_HIGH_PASS = 17, - /* Common mode on Port_A disabled/enabled */ - CONTROL_FLAG_PORT_A_COMMON_MODE = 18, - /* Common mode on Port_D disabled/enabled */ - CONTROL_FLAG_PORT_D_COMMON_MODE = 19, - /* Impedance for ramp generator on Port_A 16 Ohm/10K Ohm */ - CONTROL_FLAG_PORT_A_10KOHM_LOAD = 20, - /* Impedance for ramp generator on Port_D, 16 Ohm/10K Ohm */ - CONTROL_FLAG_PORT_D_10K0HM_LOAD = 21, - /* ASI rate is 48kHz/96kHz */ - CONTROL_FLAG_ASI_96KHZ = 22, - /* DAC power settings able to control attached ports no/yes */ - CONTROL_FLAG_DACS_CONTROL_PORTS = 23, - /* Clock Stop OK reporting is disabled/enabled */ - CONTROL_FLAG_CONTROL_STOP_OK_ENABLE = 24, - /* Number of control flags */ - CONTROL_FLAGS_MAX = (CONTROL_FLAG_CONTROL_STOP_OK_ENABLE+1) -}; - -/* - * Control parameter IDs - */ -enum control_parameter_id { - /* 0: force HDA, 1: allow DSP if HDA Spdif1Out stream is idle */ - CONTROL_PARAM_SPDIF1_SOURCE = 2, - - /* Stream Control */ - - /* Select stream with the given ID */ - CONTROL_PARAM_STREAM_ID = 24, - /* Source connection point for the selected stream */ - CONTROL_PARAM_STREAM_SOURCE_CONN_POINT = 25, - /* Destination connection point for the selected stream */ - CONTROL_PARAM_STREAM_DEST_CONN_POINT = 26, - /* Number of audio channels in the selected stream */ - CONTROL_PARAM_STREAMS_CHANNELS = 27, - /*Enable control for the selected stream */ - CONTROL_PARAM_STREAM_CONTROL = 28, - - /* Connection Point Control */ - - /* Select connection point with the given ID */ - CONTROL_PARAM_CONN_POINT_ID = 29, - /* Connection point sample rate */ - CONTROL_PARAM_CONN_POINT_SAMPLE_RATE = 30, - - /* Node Control */ - - /* Select HDA node with the given ID */ - CONTROL_PARAM_NODE_ID = 31 -}; - -/* - * Dsp Io Status codes - */ -enum hda_vendor_status_dspio { - /* Success */ - VENDOR_STATUS_DSPIO_OK = 0x00, - /* Busy, unable to accept new command, the host must retry */ - VENDOR_STATUS_DSPIO_BUSY = 0x01, - /* SCP command queue is full */ - VENDOR_STATUS_DSPIO_SCP_COMMAND_QUEUE_FULL = 0x02, - /* SCP response queue is empty */ - VENDOR_STATUS_DSPIO_SCP_RESPONSE_QUEUE_EMPTY = 0x03 -}; - -/* - * Chip Io Status codes - */ -enum hda_vendor_status_chipio { - /* Success */ - VENDOR_STATUS_CHIPIO_OK = 0x00, - /* Busy, unable to accept new command, the host must retry */ - VENDOR_STATUS_CHIPIO_BUSY = 0x01 -}; - -/* - * CA0132 sample rate - */ -enum ca0132_sample_rate { - SR_6_000 = 0x00, - SR_8_000 = 0x01, - SR_9_600 = 0x02, - SR_11_025 = 0x03, - SR_16_000 = 0x04, - SR_22_050 = 0x05, - SR_24_000 = 0x06, - SR_32_000 = 0x07, - SR_44_100 = 0x08, - SR_48_000 = 0x09, - SR_88_200 = 0x0A, - SR_96_000 = 0x0B, - SR_144_000 = 0x0C, - SR_176_400 = 0x0D, - SR_192_000 = 0x0E, - SR_384_000 = 0x0F, - - SR_COUNT = 0x10, - - SR_RATE_UNKNOWN = 0x1F -}; - -/* - * Scp Helper function - */ -enum get_set { - IS_SET = 0, - IS_GET = 1, -}; - -/* - * Duplicated from ca0110 codec - */ - -static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) -{ - if (pin) { - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP); - if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP) - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); - } - if (dac) - snd_hda_codec_write(codec, dac, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO); -} - -static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc) -{ - if (pin) { - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - PIN_VREF80); - if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP) - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(0)); - } - if (adc) - snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(0)); -} - -static char *dirstr[2] = { "Playback", "Capture" }; - -static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx, - int chan, int dir) -{ - char namestr[44]; - int type = dir ? HDA_INPUT : HDA_OUTPUT; - struct snd_kcontrol_new knew = - HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type); - sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]); - return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); -} - -static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx, - int chan, int dir) -{ - char namestr[44]; - int type = dir ? HDA_INPUT : HDA_OUTPUT; - struct snd_kcontrol_new knew = - HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type); - sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]); - return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); -} - -#define add_out_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 0) -#define add_out_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 0) -#define add_in_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 1) -#define add_in_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 1) -#define add_mono_switch(codec, nid, pfx, chan) \ - _add_switch(codec, nid, pfx, chan, 0) -#define add_mono_volume(codec, nid, pfx, chan) \ - _add_volume(codec, nid, pfx, chan, 0) -#define add_in_mono_switch(codec, nid, pfx, chan) \ - _add_switch(codec, nid, pfx, chan, 1) -#define add_in_mono_volume(codec, nid, pfx, chan) \ - _add_volume(codec, nid, pfx, chan, 1) - - -/* - * CA0132 specific - */ - -struct ca0132_spec { - struct auto_pin_cfg autocfg; - struct hda_multi_out multiout; - hda_nid_t out_pins[AUTO_CFG_MAX_OUTS]; - hda_nid_t dacs[AUTO_CFG_MAX_OUTS]; - hda_nid_t hp_dac; - hda_nid_t input_pins[AUTO_PIN_LAST]; - hda_nid_t adcs[AUTO_PIN_LAST]; - hda_nid_t dig_out; - hda_nid_t dig_in; - unsigned int num_inputs; - long curr_hp_switch; - long curr_hp_volume[2]; - long curr_speaker_switch; - struct mutex chipio_mutex; - const char *input_labels[AUTO_PIN_LAST]; - struct hda_pcm pcm_rec[2]; /* PCM information */ -}; - -/* Chip access helper function */ -static int chipio_send(struct hda_codec *codec, - unsigned int reg, - unsigned int data) -{ - unsigned int res; - int retry = 50; - - /* send bits of data specified by reg */ - do { - res = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0, - reg, data); - if (res == VENDOR_STATUS_CHIPIO_OK) - return 0; - } while (--retry); - return -EIO; -} - -/* - * Write chip address through the vendor widget -- NOT protected by the Mutex! - */ -static int chipio_write_address(struct hda_codec *codec, - unsigned int chip_addx) -{ - int res; - - /* send low 16 bits of the address */ - res = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_LOW, - chip_addx & 0xffff); - - if (res != -EIO) { - /* send high 16 bits of the address */ - res = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_HIGH, - chip_addx >> 16); - } - - return res; -} - -/* - * Write data through the vendor widget -- NOT protected by the Mutex! - */ - -static int chipio_write_data(struct hda_codec *codec, unsigned int data) -{ - int res; - - /* send low 16 bits of the data */ - res = chipio_send(codec, VENDOR_CHIPIO_DATA_LOW, data & 0xffff); - - if (res != -EIO) { - /* send high 16 bits of the data */ - res = chipio_send(codec, VENDOR_CHIPIO_DATA_HIGH, - data >> 16); - } - - return res; -} - -/* - * Read data through the vendor widget -- NOT protected by the Mutex! - */ -static int chipio_read_data(struct hda_codec *codec, unsigned int *data) -{ - int res; - - /* post read */ - res = chipio_send(codec, VENDOR_CHIPIO_HIC_POST_READ, 0); - - if (res != -EIO) { - /* read status */ - res = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0); - } - - if (res != -EIO) { - /* read data */ - *data = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_HIC_READ_DATA, - 0); - } - - return res; -} - -/* - * Write given value to the given address through the chip I/O widget. - * protected by the Mutex - */ -static int chipio_write(struct hda_codec *codec, - unsigned int chip_addx, const unsigned int data) -{ - struct ca0132_spec *spec = codec->spec; - int err; - - mutex_lock(&spec->chipio_mutex); - - /* write the address, and if successful proceed to write data */ - err = chipio_write_address(codec, chip_addx); - if (err < 0) - goto exit; - - err = chipio_write_data(codec, data); - if (err < 0) - goto exit; - -exit: - mutex_unlock(&spec->chipio_mutex); - return err; -} - -/* - * Read the given address through the chip I/O widget - * protected by the Mutex - */ -static int chipio_read(struct hda_codec *codec, - unsigned int chip_addx, unsigned int *data) -{ - struct ca0132_spec *spec = codec->spec; - int err; - - mutex_lock(&spec->chipio_mutex); - - /* write the address, and if successful proceed to write data */ - err = chipio_write_address(codec, chip_addx); - if (err < 0) - goto exit; - - err = chipio_read_data(codec, data); - if (err < 0) - goto exit; - -exit: - mutex_unlock(&spec->chipio_mutex); - return err; -} - -/* - * PCM stuffs - */ -static void ca0132_setup_stream(struct hda_codec *codec, hda_nid_t nid, - u32 stream_tag, - int channel_id, int format) -{ - unsigned int oldval, newval; - - if (!nid) - return; - - snd_printdd("ca0132_setup_stream: " - "NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n", - nid, stream_tag, channel_id, format); - - /* update the format-id if changed */ - oldval = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_STREAM_FORMAT, - 0); - if (oldval != format) { - msleep(20); - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_STREAM_FORMAT, - format); - } - - oldval = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); - newval = (stream_tag << 4) | channel_id; - if (oldval != newval) { - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_CHANNEL_STREAMID, - newval); - } -} - -static void ca0132_cleanup_stream(struct hda_codec *codec, hda_nid_t nid) -{ - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0); - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0); -} - -/* - * PCM callbacks - */ -static int ca0132_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 ca0132_spec *spec = codec->spec; - - ca0132_setup_stream(codec, spec->dacs[0], stream_tag, 0, format); - - return 0; -} - -static int ca0132_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ca0132_spec *spec = codec->spec; - - ca0132_cleanup_stream(codec, spec->dacs[0]); - - return 0; -} - -/* - * Digital out - */ -static int ca0132_dig_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 ca0132_spec *spec = codec->spec; - - ca0132_setup_stream(codec, spec->dig_out, stream_tag, 0, format); - - return 0; -} - -static int ca0132_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ca0132_spec *spec = codec->spec; - - ca0132_cleanup_stream(codec, spec->dig_out); - - return 0; -} - -/* - * Analog capture - */ -static int ca0132_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct ca0132_spec *spec = codec->spec; - - ca0132_setup_stream(codec, spec->adcs[substream->number], - stream_tag, 0, format); - - return 0; -} - -static int ca0132_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ca0132_spec *spec = codec->spec; - - ca0132_cleanup_stream(codec, spec->adcs[substream->number]); - - return 0; -} - -/* - * Digital capture - */ -static int ca0132_dig_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct ca0132_spec *spec = codec->spec; - - ca0132_setup_stream(codec, spec->dig_in, stream_tag, 0, format); - - return 0; -} - -static int ca0132_dig_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ca0132_spec *spec = codec->spec; - - ca0132_cleanup_stream(codec, spec->dig_in); - - return 0; -} - -/* - */ -static struct hda_pcm_stream ca0132_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .ops = { - .prepare = ca0132_playback_pcm_prepare, - .cleanup = ca0132_playback_pcm_cleanup - }, -}; - -static struct hda_pcm_stream ca0132_pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .ops = { - .prepare = ca0132_capture_pcm_prepare, - .cleanup = ca0132_capture_pcm_cleanup - }, -}; - -static struct hda_pcm_stream ca0132_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .ops = { - .prepare = ca0132_dig_playback_pcm_prepare, - .cleanup = ca0132_dig_playback_pcm_cleanup - }, -}; - -static struct hda_pcm_stream ca0132_pcm_digital_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .ops = { - .prepare = ca0132_dig_capture_pcm_prepare, - .cleanup = ca0132_dig_capture_pcm_cleanup - }, -}; - -static int ca0132_build_pcms(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; - - codec->pcm_info = info; - codec->num_pcms = 0; - - info->name = "CA0132 Analog"; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0132_pcm_analog_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0]; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = - spec->multiout.max_channels; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_inputs; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0]; - codec->num_pcms++; - - if (!spec->dig_out && !spec->dig_in) - return 0; - - info++; - info->name = "CA0132 Digital"; - info->pcm_type = HDA_PCM_TYPE_SPDIF; - if (spec->dig_out) { - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = - ca0132_pcm_digital_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out; - } - if (spec->dig_in) { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = - ca0132_pcm_digital_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in; - } - codec->num_pcms++; - - return 0; -} - -#define REG_CODEC_MUTE 0x18b014 -#define REG_CODEC_HP_VOL_L 0x18b070 -#define REG_CODEC_HP_VOL_R 0x18b074 - -static int ca0132_hp_switch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - long *valp = ucontrol->value.integer.value; - - *valp = spec->curr_hp_switch; - return 0; -} - -static int ca0132_hp_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - long *valp = ucontrol->value.integer.value; - unsigned int data; - int err; - - /* any change? */ - if (spec->curr_hp_switch == *valp) - return 0; - - snd_hda_power_up(codec); - - err = chipio_read(codec, REG_CODEC_MUTE, &data); - if (err < 0) - goto exit; - - /* *valp 0 is mute, 1 is unmute */ - data = (data & 0x7f) | (*valp ? 0 : 0x80); - err = chipio_write(codec, REG_CODEC_MUTE, data); - if (err < 0) - goto exit; - - spec->curr_hp_switch = *valp; - - exit: - snd_hda_power_down(codec); - return err < 0 ? err : 1; -} - -static int ca0132_speaker_switch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - long *valp = ucontrol->value.integer.value; - - *valp = spec->curr_speaker_switch; - return 0; -} - -static int ca0132_speaker_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - long *valp = ucontrol->value.integer.value; - unsigned int data; - int err; - - /* any change? */ - if (spec->curr_speaker_switch == *valp) - return 0; - - snd_hda_power_up(codec); - - err = chipio_read(codec, REG_CODEC_MUTE, &data); - if (err < 0) - goto exit; - - /* *valp 0 is mute, 1 is unmute */ - data = (data & 0xef) | (*valp ? 0 : 0x10); - err = chipio_write(codec, REG_CODEC_MUTE, data); - if (err < 0) - goto exit; - - spec->curr_speaker_switch = *valp; - - exit: - snd_hda_power_down(codec); - return err < 0 ? err : 1; -} - -static int ca0132_hp_volume_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - long *valp = ucontrol->value.integer.value; - - *valp++ = spec->curr_hp_volume[0]; - *valp = spec->curr_hp_volume[1]; - return 0; -} - -static int ca0132_hp_volume_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - long *valp = ucontrol->value.integer.value; - long left_vol, right_vol; - unsigned int data; - int val; - int err; - - left_vol = *valp++; - right_vol = *valp; - - /* any change? */ - if ((spec->curr_hp_volume[0] == left_vol) && - (spec->curr_hp_volume[1] == right_vol)) - return 0; - - snd_hda_power_up(codec); - - err = chipio_read(codec, REG_CODEC_HP_VOL_L, &data); - if (err < 0) - goto exit; - - val = 31 - left_vol; - data = (data & 0xe0) | val; - err = chipio_write(codec, REG_CODEC_HP_VOL_L, data); - if (err < 0) - goto exit; - - val = 31 - right_vol; - data = (data & 0xe0) | val; - err = chipio_write(codec, REG_CODEC_HP_VOL_R, data); - if (err < 0) - goto exit; - - spec->curr_hp_volume[0] = left_vol; - spec->curr_hp_volume[1] = right_vol; - - exit: - snd_hda_power_down(codec); - return err < 0 ? err : 1; -} - -static int add_hp_switch(struct hda_codec *codec, hda_nid_t nid) -{ - struct snd_kcontrol_new knew = - HDA_CODEC_MUTE_MONO("Headphone Playback Switch", - nid, 1, 0, HDA_OUTPUT); - knew.get = ca0132_hp_switch_get; - knew.put = ca0132_hp_switch_put; - return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); -} - -static int add_hp_volume(struct hda_codec *codec, hda_nid_t nid) -{ - struct snd_kcontrol_new knew = - HDA_CODEC_VOLUME_MONO("Headphone Playback Volume", - nid, 3, 0, HDA_OUTPUT); - knew.get = ca0132_hp_volume_get; - knew.put = ca0132_hp_volume_put; - return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); -} - -static int add_speaker_switch(struct hda_codec *codec, hda_nid_t nid) -{ - struct snd_kcontrol_new knew = - HDA_CODEC_MUTE_MONO("Speaker Playback Switch", - nid, 1, 0, HDA_OUTPUT); - knew.get = ca0132_speaker_switch_get; - knew.put = ca0132_speaker_switch_put; - return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); -} - -static void ca0132_fix_hp_caps(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int caps; - - /* set mute-capable, 1db step, 32 steps, ofs 6 */ - caps = 0x80031f06; - snd_hda_override_amp_caps(codec, cfg->hp_pins[0], HDA_OUTPUT, caps); -} - -static int ca0132_build_controls(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i, err; - - if (spec->multiout.num_dacs) { - err = add_speaker_switch(codec, spec->out_pins[0]); - if (err < 0) - return err; - } - - if (cfg->hp_outs) { - ca0132_fix_hp_caps(codec); - err = add_hp_switch(codec, cfg->hp_pins[0]); - if (err < 0) - return err; - err = add_hp_volume(codec, cfg->hp_pins[0]); - if (err < 0) - return err; - } - - for (i = 0; i < spec->num_inputs; i++) { - const char *label = spec->input_labels[i]; - - err = add_in_switch(codec, spec->adcs[i], label); - if (err < 0) - return err; - err = add_in_volume(codec, spec->adcs[i], label); - if (err < 0) - return err; - if (cfg->inputs[i].type == AUTO_PIN_MIC) { - /* add Mic-Boost */ - err = add_in_mono_volume(codec, spec->input_pins[i], - "Mic Boost", 1); - if (err < 0) - return err; - } - } - - if (spec->dig_out) { - err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out, - spec->dig_out); - if (err < 0) - return err; - err = add_out_volume(codec, spec->dig_out, "IEC958"); - if (err < 0) - return err; - } - - if (spec->dig_in) { - err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in); - if (err < 0) - return err; - err = add_in_volume(codec, spec->dig_in, "IEC958"); - if (err < 0) - return err; - } - return 0; -} - - -static void ca0132_set_ct_ext(struct hda_codec *codec, int enable) -{ - /* Set Creative extension */ - snd_printdd("SET CREATIVE EXTENSION\n"); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE, - enable); - msleep(20); -} - - -static void ca0132_config(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - - /* line-outs */ - cfg->line_outs = 1; - cfg->line_out_pins[0] = 0x0b; /* front */ - cfg->line_out_type = AUTO_PIN_LINE_OUT; - - spec->dacs[0] = 0x02; - spec->out_pins[0] = 0x0b; - spec->multiout.dac_nids = spec->dacs; - spec->multiout.num_dacs = 1; - spec->multiout.max_channels = 2; - - /* headphone */ - cfg->hp_outs = 1; - cfg->hp_pins[0] = 0x0f; - - spec->hp_dac = 0; - spec->multiout.hp_nid = 0; - - /* inputs */ - cfg->num_inputs = 2; /* Mic-in and line-in */ - cfg->inputs[0].pin = 0x12; - cfg->inputs[0].type = AUTO_PIN_MIC; - cfg->inputs[1].pin = 0x11; - cfg->inputs[1].type = AUTO_PIN_LINE_IN; - - /* Mic-in */ - spec->input_pins[0] = 0x12; - spec->input_labels[0] = "Mic-In"; - spec->adcs[0] = 0x07; - - /* Line-In */ - spec->input_pins[1] = 0x11; - spec->input_labels[1] = "Line-In"; - spec->adcs[1] = 0x08; - spec->num_inputs = 2; -} - -static void ca0132_init_chip(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - - mutex_init(&spec->chipio_mutex); -} - -static void ca0132_exit_chip(struct hda_codec *codec) -{ - /* put any chip cleanup stuffs here. */ -} - -static int ca0132_init(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - for (i = 0; i < spec->multiout.num_dacs; i++) { - init_output(codec, spec->out_pins[i], - spec->multiout.dac_nids[i]); - } - init_output(codec, cfg->hp_pins[0], spec->hp_dac); - init_output(codec, cfg->dig_out_pins[0], spec->dig_out); - - for (i = 0; i < spec->num_inputs; i++) - init_input(codec, spec->input_pins[i], spec->adcs[i]); - - init_input(codec, cfg->dig_in_pin, spec->dig_in); - - ca0132_set_ct_ext(codec, 1); - - return 0; -} - - -static void ca0132_free(struct hda_codec *codec) -{ - ca0132_set_ct_ext(codec, 0); - ca0132_exit_chip(codec); - kfree(codec->spec); -} - -static struct hda_codec_ops ca0132_patch_ops = { - .build_controls = ca0132_build_controls, - .build_pcms = ca0132_build_pcms, - .init = ca0132_init, - .free = ca0132_free, -}; - - - -static int patch_ca0132(struct hda_codec *codec) -{ - struct ca0132_spec *spec; - - snd_printdd("patch_ca0132\n"); - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return -ENOMEM; - codec->spec = spec; - - ca0132_init_chip(codec); - - ca0132_config(codec); - - codec->patch_ops = ca0132_patch_ops; - - return 0; -} - -/* - * patch entries - */ -static struct hda_codec_preset snd_hda_preset_ca0132[] = { - { .id = 0x11020011, .name = "CA0132", .patch = patch_ca0132 }, - {} /* terminator */ -}; - -MODULE_ALIAS("snd-hda-codec-id:11020011"); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Creative CA0132, CA0132 HD-audio codec"); - -static struct hda_codec_preset_list ca0132_list = { - .preset = snd_hda_preset_ca0132, - .owner = THIS_MODULE, -}; - -static int __init patch_ca0132_init(void) -{ - return snd_hda_add_codec_preset(&ca0132_list); -} - -static void __exit patch_ca0132_exit(void) -{ - snd_hda_delete_codec_preset(&ca0132_list); -} - -module_init(patch_ca0132_init) -module_exit(patch_ca0132_exit) diff --git a/ANDROID_3.4.5/sound/pci/hda/patch_cirrus.c b/ANDROID_3.4.5/sound/pci/hda/patch_cirrus.c deleted file mode 100644 index c83ccdba..00000000 --- a/ANDROID_3.4.5/sound/pci/hda/patch_cirrus.c +++ /dev/null @@ -1,2053 +0,0 @@ -/* - * HD audio interface patch for Cirrus Logic CS420x chip - * - * Copyright (c) 2009 Takashi Iwai <tiwai@suse.de> - * - * 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 <linux/delay.h> -#include <linux/slab.h> -#include <linux/pci.h> -#include <linux/module.h> -#include <sound/core.h> -#include "hda_codec.h" -#include "hda_local.h" -#include "hda_jack.h" -#include <sound/tlv.h> - -/* - */ - -struct cs_spec { - int board_config; - struct auto_pin_cfg autocfg; - struct hda_multi_out multiout; - struct snd_kcontrol *vmaster_sw; - struct snd_kcontrol *vmaster_vol; - - hda_nid_t dac_nid[AUTO_CFG_MAX_OUTS]; - hda_nid_t slave_dig_outs[2]; - - unsigned int input_idx[AUTO_PIN_LAST]; - unsigned int capsrc_idx[AUTO_PIN_LAST]; - hda_nid_t adc_nid[AUTO_PIN_LAST]; - unsigned int adc_idx[AUTO_PIN_LAST]; - unsigned int num_inputs; - unsigned int cur_input; - unsigned int automic_idx; - hda_nid_t cur_adc; - unsigned int cur_adc_stream_tag; - unsigned int cur_adc_format; - hda_nid_t dig_in; - - const struct hda_bind_ctls *capture_bind[2]; - - unsigned int gpio_mask; - unsigned int gpio_dir; - unsigned int gpio_data; - unsigned int gpio_eapd_hp; /* EAPD GPIO bit for headphones */ - unsigned int gpio_eapd_speaker; /* EAPD GPIO bit for speakers */ - - struct hda_pcm pcm_rec[2]; /* PCM information */ - - unsigned int hp_detect:1; - unsigned int mic_detect:1; - /* CS421x */ - unsigned int spdif_detect:1; - unsigned int sense_b:1; - hda_nid_t vendor_nid; - struct hda_input_mux input_mux; - unsigned int last_input; -}; - -/* available models with CS420x */ -enum { - CS420X_MBP53, - CS420X_MBP55, - CS420X_IMAC27, - CS420X_IMAC27_122, - CS420X_APPLE, - CS420X_AUTO, - CS420X_MODELS -}; - -/* CS421x boards */ -enum { - CS421X_CDB4210, - CS421X_MODELS -}; - -/* Vendor-specific processing widget */ -#define CS420X_VENDOR_NID 0x11 -#define CS_DIG_OUT1_PIN_NID 0x10 -#define CS_DIG_OUT2_PIN_NID 0x15 -#define CS_DMIC1_PIN_NID 0x12 -#define CS_DMIC2_PIN_NID 0x0e - -/* coef indices */ -#define IDX_SPDIF_STAT 0x0000 -#define IDX_SPDIF_CTL 0x0001 -#define IDX_ADC_CFG 0x0002 -/* SZC bitmask, 4 modes below: - * 0 = immediate, - * 1 = digital immediate, analog zero-cross - * 2 = digtail & analog soft-ramp - * 3 = digital soft-ramp, analog zero-cross - */ -#define CS_COEF_ADC_SZC_MASK (3 << 0) -#define CS_COEF_ADC_MIC_SZC_MODE (3 << 0) /* SZC setup for mic */ -#define CS_COEF_ADC_LI_SZC_MODE (3 << 0) /* SZC setup for line-in */ -/* PGA mode: 0 = differential, 1 = signle-ended */ -#define CS_COEF_ADC_MIC_PGA_MODE (1 << 5) /* PGA setup for mic */ -#define CS_COEF_ADC_LI_PGA_MODE (1 << 6) /* PGA setup for line-in */ -#define IDX_DAC_CFG 0x0003 -/* SZC bitmask, 4 modes below: - * 0 = Immediate - * 1 = zero-cross - * 2 = soft-ramp - * 3 = soft-ramp on zero-cross - */ -#define CS_COEF_DAC_HP_SZC_MODE (3 << 0) /* nid 0x02 */ -#define CS_COEF_DAC_LO_SZC_MODE (3 << 2) /* nid 0x03 */ -#define CS_COEF_DAC_SPK_SZC_MODE (3 << 4) /* nid 0x04 */ - -#define IDX_BEEP_CFG 0x0004 -/* 0x0008 - test reg key */ -/* 0x0009 - 0x0014 -> 12 test regs */ -/* 0x0015 - visibility reg */ - -/* - * Cirrus Logic CS4210 - * - * 1 DAC => HP(sense) / Speakers, - * 1 ADC <= LineIn(sense) / MicIn / DMicIn, - * 1 SPDIF OUT => SPDIF Trasmitter(sense) -*/ -#define CS4210_DAC_NID 0x02 -#define CS4210_ADC_NID 0x03 -#define CS4210_VENDOR_NID 0x0B -#define CS421X_DMIC_PIN_NID 0x09 /* Port E */ -#define CS421X_SPDIF_PIN_NID 0x0A /* Port H */ - -#define CS421X_IDX_DEV_CFG 0x01 -#define CS421X_IDX_ADC_CFG 0x02 -#define CS421X_IDX_DAC_CFG 0x03 -#define CS421X_IDX_SPK_CTL 0x04 - -#define SPDIF_EVENT 0x04 - -/* Cirrus Logic CS4213 is like CS4210 but does not have SPDIF input/output */ -#define CS4213_VENDOR_NID 0x09 - - -static inline int cs_vendor_coef_get(struct hda_codec *codec, unsigned int idx) -{ - struct cs_spec *spec = codec->spec; - snd_hda_codec_write(codec, spec->vendor_nid, 0, - AC_VERB_SET_COEF_INDEX, idx); - return snd_hda_codec_read(codec, spec->vendor_nid, 0, - AC_VERB_GET_PROC_COEF, 0); -} - -static inline void cs_vendor_coef_set(struct hda_codec *codec, unsigned int idx, - unsigned int coef) -{ - struct cs_spec *spec = codec->spec; - snd_hda_codec_write(codec, spec->vendor_nid, 0, - AC_VERB_SET_COEF_INDEX, idx); - snd_hda_codec_write(codec, spec->vendor_nid, 0, - AC_VERB_SET_PROC_COEF, coef); -} - - -#define HP_EVENT 1 -#define MIC_EVENT 2 - -/* - * PCM callbacks - */ -static int cs_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct cs_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, - hinfo); -} - -static int cs_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 cs_spec *spec = codec->spec; - return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, - stream_tag, format, substream); -} - -static int cs_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct cs_spec *spec = codec->spec; - return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); -} - -/* - * Digital out - */ -static int cs_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct cs_spec *spec = codec->spec; - return snd_hda_multi_out_dig_open(codec, &spec->multiout); -} - -static int cs_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct cs_spec *spec = codec->spec; - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} - -static int cs_dig_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 cs_spec *spec = codec->spec; - return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, - format, substream); -} - -static int cs_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct cs_spec *spec = codec->spec; - return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); -} - -static void cs_update_input_select(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - if (spec->cur_adc) - snd_hda_codec_write(codec, spec->cur_adc, 0, - AC_VERB_SET_CONNECT_SEL, - spec->adc_idx[spec->cur_input]); -} - -/* - * Analog capture - */ -static int cs_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct cs_spec *spec = codec->spec; - spec->cur_adc = spec->adc_nid[spec->cur_input]; - spec->cur_adc_stream_tag = stream_tag; - spec->cur_adc_format = format; - cs_update_input_select(codec); - snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format); - return 0; -} - -static int cs_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct cs_spec *spec = codec->spec; - snd_hda_codec_cleanup_stream(codec, spec->cur_adc); - spec->cur_adc = 0; - return 0; -} - -/* - */ -static const struct hda_pcm_stream cs_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .ops = { - .open = cs_playback_pcm_open, - .prepare = cs_playback_pcm_prepare, - .cleanup = cs_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream cs_pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .ops = { - .prepare = cs_capture_pcm_prepare, - .cleanup = cs_capture_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream cs_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .ops = { - .open = cs_dig_playback_pcm_open, - .close = cs_dig_playback_pcm_close, - .prepare = cs_dig_playback_pcm_prepare, - .cleanup = cs_dig_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream cs_pcm_digital_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, -}; - -static int cs_build_pcms(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; - - codec->pcm_info = info; - codec->num_pcms = 0; - - info->name = "Cirrus Analog"; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = cs_pcm_analog_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dac_nid[0]; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = - spec->multiout.max_channels; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = cs_pcm_analog_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = - spec->adc_nid[spec->cur_input]; - codec->num_pcms++; - - if (!spec->multiout.dig_out_nid && !spec->dig_in) - return 0; - - info++; - info->name = "Cirrus Digital"; - info->pcm_type = spec->autocfg.dig_out_type[0]; - if (!info->pcm_type) - info->pcm_type = HDA_PCM_TYPE_SPDIF; - if (spec->multiout.dig_out_nid) { - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = - cs_pcm_digital_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = - spec->multiout.dig_out_nid; - } - if (spec->dig_in) { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = - cs_pcm_digital_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in; - } - codec->num_pcms++; - - return 0; -} - -/* - * parse codec topology - */ - -static hda_nid_t get_dac(struct hda_codec *codec, hda_nid_t pin) -{ - hda_nid_t dac; - if (!pin) - return 0; - if (snd_hda_get_connections(codec, pin, &dac, 1) != 1) - return 0; - return dac; -} - -static int is_ext_mic(struct hda_codec *codec, unsigned int idx) -{ - struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t pin = cfg->inputs[idx].pin; - unsigned int val; - if (!is_jack_detectable(codec, pin)) - return 0; - val = snd_hda_codec_get_pincfg(codec, pin); - return (snd_hda_get_input_pin_attr(val) != INPUT_PIN_ATTR_INT); -} - -static hda_nid_t get_adc(struct hda_codec *codec, hda_nid_t pin, - unsigned int *idxp) -{ - int i, idx; - hda_nid_t nid; - - nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, nid++) { - unsigned int type; - type = get_wcaps_type(get_wcaps(codec, nid)); - if (type != AC_WID_AUD_IN) - continue; - idx = snd_hda_get_conn_index(codec, nid, pin, false); - if (idx >= 0) { - *idxp = idx; - return nid; - } - } - return 0; -} - -static int is_active_pin(struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int val; - val = snd_hda_codec_get_pincfg(codec, nid); - return (get_defcfg_connect(val) != AC_JACK_PORT_NONE); -} - -static int parse_output(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i, extra_nids; - hda_nid_t dac; - - for (i = 0; i < cfg->line_outs; i++) { - dac = get_dac(codec, cfg->line_out_pins[i]); - if (!dac) - break; - spec->dac_nid[i] = dac; - } - spec->multiout.num_dacs = i; - spec->multiout.dac_nids = spec->dac_nid; - spec->multiout.max_channels = i * 2; - - /* add HP and speakers */ - extra_nids = 0; - for (i = 0; i < cfg->hp_outs; i++) { - dac = get_dac(codec, cfg->hp_pins[i]); - if (!dac) - break; - if (!i) - spec->multiout.hp_nid = dac; - else - spec->multiout.extra_out_nid[extra_nids++] = dac; - } - for (i = 0; i < cfg->speaker_outs; i++) { - dac = get_dac(codec, cfg->speaker_pins[i]); - if (!dac) - break; - spec->multiout.extra_out_nid[extra_nids++] = dac; - } - - if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { - cfg->speaker_outs = cfg->line_outs; - memcpy(cfg->speaker_pins, cfg->line_out_pins, - sizeof(cfg->speaker_pins)); - cfg->line_outs = 0; - } - - return 0; -} - -static int parse_input(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t pin = cfg->inputs[i].pin; - spec->input_idx[spec->num_inputs] = i; - spec->capsrc_idx[i] = spec->num_inputs++; - spec->cur_input = i; - spec->adc_nid[i] = get_adc(codec, pin, &spec->adc_idx[i]); - } - if (!spec->num_inputs) - return 0; - - /* check whether the automatic mic switch is available */ - if (spec->num_inputs == 2 && - cfg->inputs[0].type == AUTO_PIN_MIC && - cfg->inputs[1].type == AUTO_PIN_MIC) { - if (is_ext_mic(codec, cfg->inputs[0].pin)) { - if (!is_ext_mic(codec, cfg->inputs[1].pin)) { - spec->mic_detect = 1; - spec->automic_idx = 0; - } - } else { - if (is_ext_mic(codec, cfg->inputs[1].pin)) { - spec->mic_detect = 1; - spec->automic_idx = 1; - } - } - } - return 0; -} - - -static int parse_digital_output(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t nid; - - if (!cfg->dig_outs) - return 0; - if (snd_hda_get_connections(codec, cfg->dig_out_pins[0], &nid, 1) < 1) - return 0; - spec->multiout.dig_out_nid = nid; - spec->multiout.share_spdif = 1; - if (cfg->dig_outs > 1 && - snd_hda_get_connections(codec, cfg->dig_out_pins[1], &nid, 1) > 0) { - spec->slave_dig_outs[0] = nid; - codec->slave_dig_outs = spec->slave_dig_outs; - } - return 0; -} - -static int parse_digital_input(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int idx; - - if (cfg->dig_in_pin) - spec->dig_in = get_adc(codec, cfg->dig_in_pin, &idx); - return 0; -} - -/* - * create mixer controls - */ - -static const char * const dir_sfx[2] = { "Playback", "Capture" }; - -static int add_mute(struct hda_codec *codec, const char *name, int index, - unsigned int pval, int dir, struct snd_kcontrol **kctlp) -{ - char tmp[44]; - struct snd_kcontrol_new knew = - HDA_CODEC_MUTE_IDX(tmp, index, 0, 0, HDA_OUTPUT); - knew.private_value = pval; - snprintf(tmp, sizeof(tmp), "%s %s Switch", name, dir_sfx[dir]); - *kctlp = snd_ctl_new1(&knew, codec); - (*kctlp)->id.subdevice = HDA_SUBDEV_AMP_FLAG; - return snd_hda_ctl_add(codec, 0, *kctlp); -} - -static int add_volume(struct hda_codec *codec, const char *name, - int index, unsigned int pval, int dir, - struct snd_kcontrol **kctlp) -{ - char tmp[44]; - struct snd_kcontrol_new knew = - HDA_CODEC_VOLUME_IDX(tmp, index, 0, 0, HDA_OUTPUT); - knew.private_value = pval; - snprintf(tmp, sizeof(tmp), "%s %s Volume", name, dir_sfx[dir]); - *kctlp = snd_ctl_new1(&knew, codec); - (*kctlp)->id.subdevice = HDA_SUBDEV_AMP_FLAG; - return snd_hda_ctl_add(codec, 0, *kctlp); -} - -static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac) -{ - unsigned int caps; - - /* set the upper-limit for mixer amp to 0dB */ - caps = query_amp_caps(codec, dac, HDA_OUTPUT); - caps &= ~(0x7f << AC_AMPCAP_NUM_STEPS_SHIFT); - caps |= ((caps >> AC_AMPCAP_OFFSET_SHIFT) & 0x7f) - << AC_AMPCAP_NUM_STEPS_SHIFT; - snd_hda_override_amp_caps(codec, dac, HDA_OUTPUT, caps); -} - -static int add_vmaster(struct hda_codec *codec, hda_nid_t dac) -{ - struct cs_spec *spec = codec->spec; - unsigned int tlv[4]; - int err; - - spec->vmaster_sw = - snd_ctl_make_virtual_master("Master Playback Switch", NULL); - err = snd_hda_ctl_add(codec, dac, spec->vmaster_sw); - if (err < 0) - return err; - - snd_hda_set_vmaster_tlv(codec, dac, HDA_OUTPUT, tlv); - spec->vmaster_vol = - snd_ctl_make_virtual_master("Master Playback Volume", tlv); - err = snd_hda_ctl_add(codec, dac, spec->vmaster_vol); - if (err < 0) - return err; - return 0; -} - -static int add_output(struct hda_codec *codec, hda_nid_t dac, int idx, - int num_ctls, int type) -{ - struct cs_spec *spec = codec->spec; - const char *name; - int err, index; - struct snd_kcontrol *kctl; - static const char * const speakers[] = { - "Front Speaker", "Surround Speaker", "Bass Speaker" - }; - static const char * const line_outs[] = { - "Front Line Out", "Surround Line Out", "Bass Line Out" - }; - - fix_volume_caps(codec, dac); - if (!spec->vmaster_sw) { - err = add_vmaster(codec, dac); - if (err < 0) - return err; - } - - index = 0; - switch (type) { - case AUTO_PIN_HP_OUT: - name = "Headphone"; - index = idx; - break; - case AUTO_PIN_SPEAKER_OUT: - if (num_ctls > 1) - name = speakers[idx]; - else - name = "Speaker"; - break; - default: - if (num_ctls > 1) - name = line_outs[idx]; - else - name = "Line Out"; - break; - } - - err = add_mute(codec, name, index, - HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl); - if (err < 0) - return err; - err = snd_ctl_add_slave(spec->vmaster_sw, kctl); - if (err < 0) - return err; - - err = add_volume(codec, name, index, - HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl); - if (err < 0) - return err; - err = snd_ctl_add_slave(spec->vmaster_vol, kctl); - if (err < 0) - return err; - - return 0; -} - -static int build_output(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i, err; - - for (i = 0; i < cfg->line_outs; i++) { - err = add_output(codec, get_dac(codec, cfg->line_out_pins[i]), - i, cfg->line_outs, cfg->line_out_type); - if (err < 0) - return err; - } - for (i = 0; i < cfg->hp_outs; i++) { - err = add_output(codec, get_dac(codec, cfg->hp_pins[i]), - i, cfg->hp_outs, AUTO_PIN_HP_OUT); - if (err < 0) - return err; - } - for (i = 0; i < cfg->speaker_outs; i++) { - err = add_output(codec, get_dac(codec, cfg->speaker_pins[i]), - i, cfg->speaker_outs, AUTO_PIN_SPEAKER_OUT); - if (err < 0) - return err; - } - return 0; -} - -/* - */ - -static const struct snd_kcontrol_new cs_capture_ctls[] = { - HDA_BIND_SW("Capture Switch", 0), - HDA_BIND_VOL("Capture Volume", 0), -}; - -static int change_cur_input(struct hda_codec *codec, unsigned int idx, - int force) -{ - struct cs_spec *spec = codec->spec; - - if (spec->cur_input == idx && !force) - return 0; - if (spec->cur_adc && spec->cur_adc != spec->adc_nid[idx]) { - /* stream is running, let's swap the current ADC */ - __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); - spec->cur_adc = spec->adc_nid[idx]; - snd_hda_codec_setup_stream(codec, spec->cur_adc, - spec->cur_adc_stream_tag, 0, - spec->cur_adc_format); - } - spec->cur_input = idx; - cs_update_input_select(codec); - return 1; -} - -static int cs_capture_source_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int idx; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = spec->num_inputs; - if (uinfo->value.enumerated.item >= spec->num_inputs) - uinfo->value.enumerated.item = spec->num_inputs - 1; - idx = spec->input_idx[uinfo->value.enumerated.item]; - snd_hda_get_pin_label(codec, cfg->inputs[idx].pin, cfg, - uinfo->value.enumerated.name, - sizeof(uinfo->value.enumerated.name), NULL); - return 0; -} - -static int cs_capture_source_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct cs_spec *spec = codec->spec; - ucontrol->value.enumerated.item[0] = spec->capsrc_idx[spec->cur_input]; - return 0; -} - -static int cs_capture_source_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct cs_spec *spec = codec->spec; - unsigned int idx = ucontrol->value.enumerated.item[0]; - - if (idx >= spec->num_inputs) - return -EINVAL; - idx = spec->input_idx[idx]; - return change_cur_input(codec, idx, 0); -} - -static const struct snd_kcontrol_new cs_capture_source = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, - .info = cs_capture_source_info, - .get = cs_capture_source_get, - .put = cs_capture_source_put, -}; - -static const struct hda_bind_ctls *make_bind_capture(struct hda_codec *codec, - struct hda_ctl_ops *ops) -{ - struct cs_spec *spec = codec->spec; - struct hda_bind_ctls *bind; - int i, n; - - bind = kzalloc(sizeof(*bind) + sizeof(long) * (spec->num_inputs + 1), - GFP_KERNEL); - if (!bind) - return NULL; - bind->ops = ops; - n = 0; - for (i = 0; i < AUTO_PIN_LAST; i++) { - if (!spec->adc_nid[i]) - continue; - bind->values[n++] = - HDA_COMPOSE_AMP_VAL(spec->adc_nid[i], 3, - spec->adc_idx[i], HDA_INPUT); - } - return bind; -} - -/* add a (input-boost) volume control to the given input pin */ -static int add_input_volume_control(struct hda_codec *codec, - struct auto_pin_cfg *cfg, - int item) -{ - hda_nid_t pin = cfg->inputs[item].pin; - u32 caps; - const char *label; - struct snd_kcontrol *kctl; - - if (!(get_wcaps(codec, pin) & AC_WCAP_IN_AMP)) - return 0; - caps = query_amp_caps(codec, pin, HDA_INPUT); - caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; - if (caps <= 1) - return 0; - label = hda_get_autocfg_input_label(codec, cfg, item); - return add_volume(codec, label, 0, - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT), 1, &kctl); -} - -static int build_input(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - int i, err; - - if (!spec->num_inputs) - return 0; - - /* make bind-capture */ - spec->capture_bind[0] = make_bind_capture(codec, &snd_hda_bind_sw); - spec->capture_bind[1] = make_bind_capture(codec, &snd_hda_bind_vol); - for (i = 0; i < 2; i++) { - struct snd_kcontrol *kctl; - int n; - if (!spec->capture_bind[i]) - return -ENOMEM; - kctl = snd_ctl_new1(&cs_capture_ctls[i], codec); - if (!kctl) - return -ENOMEM; - kctl->private_value = (long)spec->capture_bind[i]; - err = snd_hda_ctl_add(codec, 0, kctl); - if (err < 0) - return err; - for (n = 0; n < AUTO_PIN_LAST; n++) { - if (!spec->adc_nid[n]) - continue; - err = snd_hda_add_nid(codec, kctl, 0, spec->adc_nid[n]); - if (err < 0) - return err; - } - } - - if (spec->num_inputs > 1 && !spec->mic_detect) { - err = snd_hda_ctl_add(codec, 0, - snd_ctl_new1(&cs_capture_source, codec)); - if (err < 0) - return err; - } - - for (i = 0; i < spec->num_inputs; i++) { - err = add_input_volume_control(codec, &spec->autocfg, i); - if (err < 0) - return err; - } - - return 0; -} - -/* - */ - -static int build_digital_output(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - int err; - - if (!spec->multiout.dig_out_nid) - return 0; - - err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid, - spec->multiout.dig_out_nid); - if (err < 0) - return err; - err = snd_hda_create_spdif_share_sw(codec, &spec->multiout); - if (err < 0) - return err; - return 0; -} - -static int build_digital_input(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - if (spec->dig_in) - return snd_hda_create_spdif_in_ctls(codec, spec->dig_in); - return 0; -} - -/* - * auto-mute and auto-mic switching - * CS421x auto-output redirecting - * HP/SPK/SPDIF - */ - -static void cs_automute(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int hp_present; - unsigned int spdif_present; - hda_nid_t nid; - int i; - - spdif_present = 0; - if (cfg->dig_outs) { - nid = cfg->dig_out_pins[0]; - if (is_jack_detectable(codec, nid)) { - /* - TODO: SPDIF output redirect when SENSE_B is enabled. - Shared (SENSE_A) jack (e.g HP/mini-TOSLINK) - assumed. - */ - if (snd_hda_jack_detect(codec, nid) - /* && spec->sense_b */) - spdif_present = 1; - } - } - - hp_present = 0; - for (i = 0; i < cfg->hp_outs; i++) { - nid = cfg->hp_pins[i]; - if (!is_jack_detectable(codec, nid)) - continue; - hp_present = snd_hda_jack_detect(codec, nid); - if (hp_present) - break; - } - - /* mute speakers if spdif or hp jack is plugged in */ - for (i = 0; i < cfg->speaker_outs; i++) { - int pin_ctl = hp_present ? 0 : PIN_OUT; - /* detect on spdif is specific to CS4210 */ - if (spdif_present && (spec->vendor_nid == CS4210_VENDOR_NID)) - pin_ctl = 0; - - nid = cfg->speaker_pins[i]; - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, pin_ctl); - } - if (spec->gpio_eapd_hp) { - unsigned int gpio = hp_present ? - spec->gpio_eapd_hp : spec->gpio_eapd_speaker; - snd_hda_codec_write(codec, 0x01, 0, - AC_VERB_SET_GPIO_DATA, gpio); - } - - /* specific to CS4210 */ - if (spec->vendor_nid == CS4210_VENDOR_NID) { - /* mute HPs if spdif jack (SENSE_B) is present */ - for (i = 0; i < cfg->hp_outs; i++) { - nid = cfg->hp_pins[i]; - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - (spdif_present && spec->sense_b) ? 0 : PIN_HP); - } - - /* SPDIF TX on/off */ - if (cfg->dig_outs) { - nid = cfg->dig_out_pins[0]; - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - spdif_present ? PIN_OUT : 0); - - } - /* Update board GPIOs if neccessary ... */ - } -} - -/* - * Auto-input redirect for CS421x - * Switch max 3 inputs of a single ADC (nid 3) -*/ - -static void cs_automic(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t nid; - unsigned int present; - - nid = cfg->inputs[spec->automic_idx].pin; - present = snd_hda_jack_detect(codec, nid); - - /* specific to CS421x, single ADC */ - if (spec->vendor_nid == CS420X_VENDOR_NID) { - if (present) - change_cur_input(codec, spec->automic_idx, 0); - else - change_cur_input(codec, !spec->automic_idx, 0); - } else { - if (present) { - if (spec->cur_input != spec->automic_idx) { - spec->last_input = spec->cur_input; - spec->cur_input = spec->automic_idx; - } - } else { - spec->cur_input = spec->last_input; - } - cs_update_input_select(codec); - } -} - -/* - */ - -static void init_output(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - /* mute first */ - for (i = 0; i < spec->multiout.num_dacs; i++) - snd_hda_codec_write(codec, spec->multiout.dac_nids[i], 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - if (spec->multiout.hp_nid) - snd_hda_codec_write(codec, spec->multiout.hp_nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) { - if (!spec->multiout.extra_out_nid[i]) - break; - snd_hda_codec_write(codec, spec->multiout.extra_out_nid[i], 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - } - - /* set appropriate pin controls */ - for (i = 0; i < cfg->line_outs; i++) - snd_hda_codec_write(codec, cfg->line_out_pins[i], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - /* HP */ - for (i = 0; i < cfg->hp_outs; i++) { - hda_nid_t nid = cfg->hp_pins[i]; - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP); - if (!cfg->speaker_outs) - continue; - if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) { - snd_hda_jack_detect_enable(codec, nid, HP_EVENT); - spec->hp_detect = 1; - } - } - - /* Speaker */ - for (i = 0; i < cfg->speaker_outs; i++) - snd_hda_codec_write(codec, cfg->speaker_pins[i], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - - /* SPDIF is enabled on presence detect for CS421x */ - if (spec->hp_detect || spec->spdif_detect) - cs_automute(codec); -} - -static void init_input(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int coef; - int i; - - for (i = 0; i < cfg->num_inputs; i++) { - unsigned int ctl; - hda_nid_t pin = cfg->inputs[i].pin; - if (!spec->adc_nid[i]) - continue; - /* set appropriate pin control and mute first */ - ctl = PIN_IN; - if (cfg->inputs[i].type == AUTO_PIN_MIC) { - unsigned int caps = snd_hda_query_pin_caps(codec, pin); - caps >>= AC_PINCAP_VREF_SHIFT; - if (caps & AC_PINCAP_VREF_80) - ctl = PIN_VREF80; - } - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, ctl); - snd_hda_codec_write(codec, spec->adc_nid[i], 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_MUTE(spec->adc_idx[i])); - if (spec->mic_detect && spec->automic_idx == i) - snd_hda_jack_detect_enable(codec, pin, MIC_EVENT); - } - /* CS420x has multiple ADC, CS421x has single ADC */ - if (spec->vendor_nid == CS420X_VENDOR_NID) { - change_cur_input(codec, spec->cur_input, 1); - if (spec->mic_detect) - cs_automic(codec); - - coef = 0x000a; /* ADC1/2 - Digital and Analog Soft Ramp */ - if (is_active_pin(codec, CS_DMIC2_PIN_NID)) - coef |= 0x0500; /* DMIC2 2 chan on, GPIO1 off */ - if (is_active_pin(codec, CS_DMIC1_PIN_NID)) - coef |= 0x1800; /* DMIC1 2 chan on, GPIO0 off - * No effect if SPDIF_OUT2 is - * selected in IDX_SPDIF_CTL. - */ - cs_vendor_coef_set(codec, IDX_ADC_CFG, coef); - } else { - if (spec->mic_detect) - cs_automic(codec); - else { - spec->cur_adc = spec->adc_nid[spec->cur_input]; - cs_update_input_select(codec); - } - } -} - -static const struct hda_verb cs_coef_init_verbs[] = { - {0x11, AC_VERB_SET_PROC_STATE, 1}, - {0x11, AC_VERB_SET_COEF_INDEX, IDX_DAC_CFG}, - {0x11, AC_VERB_SET_PROC_COEF, - (0x002a /* DAC1/2/3 SZCMode Soft Ramp */ - | 0x0040 /* Mute DACs on FIFO error */ - | 0x1000 /* Enable DACs High Pass Filter */ - | 0x0400 /* Disable Coefficient Auto increment */ - )}, - /* Beep */ - {0x11, AC_VERB_SET_COEF_INDEX, IDX_DAC_CFG}, - {0x11, AC_VERB_SET_PROC_COEF, 0x0007}, /* Enable Beep thru DAC1/2/3 */ - - {} /* terminator */ -}; - -/* Errata: CS4207 rev C0/C1/C2 Silicon - * - * http://www.cirrus.com/en/pubs/errata/ER880C3.pdf - * - * 6. At high temperature (TA > +85°C), the digital supply current (IVD) - * may be excessive (up to an additional 200 μA), which is most easily - * observed while the part is being held in reset (RESET# active low). - * - * Root Cause: At initial powerup of the device, the logic that drives - * the clock and write enable to the S/PDIF SRC RAMs is not properly - * initialized. - * Certain random patterns will cause a steady leakage current in those - * RAM cells. The issue will resolve once the SRCs are used (turned on). - * - * Workaround: The following verb sequence briefly turns on the S/PDIF SRC - * blocks, which will alleviate the issue. - */ - -static const struct hda_verb cs_errata_init_verbs[] = { - {0x01, AC_VERB_SET_POWER_STATE, 0x00}, /* AFG: D0 */ - {0x11, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */ - - {0x11, AC_VERB_SET_COEF_INDEX, 0x0008}, - {0x11, AC_VERB_SET_PROC_COEF, 0x9999}, - {0x11, AC_VERB_SET_COEF_INDEX, 0x0017}, - {0x11, AC_VERB_SET_PROC_COEF, 0xa412}, - {0x11, AC_VERB_SET_COEF_INDEX, 0x0001}, - {0x11, AC_VERB_SET_PROC_COEF, 0x0009}, - - {0x07, AC_VERB_SET_POWER_STATE, 0x00}, /* S/PDIF Rx: D0 */ - {0x08, AC_VERB_SET_POWER_STATE, 0x00}, /* S/PDIF Tx: D0 */ - - {0x11, AC_VERB_SET_COEF_INDEX, 0x0017}, - {0x11, AC_VERB_SET_PROC_COEF, 0x2412}, - {0x11, AC_VERB_SET_COEF_INDEX, 0x0008}, - {0x11, AC_VERB_SET_PROC_COEF, 0x0000}, - {0x11, AC_VERB_SET_COEF_INDEX, 0x0001}, - {0x11, AC_VERB_SET_PROC_COEF, 0x0008}, - {0x11, AC_VERB_SET_PROC_STATE, 0x00}, - -#if 0 /* Don't to set to D3 as we are in power-up sequence */ - {0x07, AC_VERB_SET_POWER_STATE, 0x03}, /* S/PDIF Rx: D3 */ - {0x08, AC_VERB_SET_POWER_STATE, 0x03}, /* S/PDIF Tx: D3 */ - /*{0x01, AC_VERB_SET_POWER_STATE, 0x03},*/ /* AFG: D3 This is already handled */ -#endif - - {} /* terminator */ -}; - -/* SPDIF setup */ -static void init_digital(struct hda_codec *codec) -{ - unsigned int coef; - - coef = 0x0002; /* SRC_MUTE soft-mute on SPDIF (if no lock) */ - coef |= 0x0008; /* Replace with mute on error */ - if (is_active_pin(codec, CS_DIG_OUT2_PIN_NID)) - coef |= 0x4000; /* RX to TX1 or TX2 Loopthru / SPDIF2 - * SPDIF_OUT2 is shared with GPIO1 and - * DMIC_SDA2. - */ - cs_vendor_coef_set(codec, IDX_SPDIF_CTL, coef); -} - -static int cs_init(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - - /* init_verb sequence for C0/C1/C2 errata*/ - snd_hda_sequence_write(codec, cs_errata_init_verbs); - - snd_hda_sequence_write(codec, cs_coef_init_verbs); - - if (spec->gpio_mask) { - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, - spec->gpio_mask); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, - spec->gpio_dir); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_data); - } - - init_output(codec); - init_input(codec); - init_digital(codec); - snd_hda_jack_report_sync(codec); - - return 0; -} - -static int cs_build_controls(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - int err; - - err = build_output(codec); - if (err < 0) - return err; - err = build_input(codec); - if (err < 0) - return err; - err = build_digital_output(codec); - if (err < 0) - return err; - err = build_digital_input(codec); - if (err < 0) - return err; - err = cs_init(codec); - if (err < 0) - return err; - - err = snd_hda_jack_add_kctls(codec, &spec->autocfg); - if (err < 0) - return err; - - return 0; -} - -static void cs_free(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - kfree(spec->capture_bind[0]); - kfree(spec->capture_bind[1]); - kfree(codec->spec); -} - -static void cs_unsol_event(struct hda_codec *codec, unsigned int res) -{ - switch (snd_hda_jack_get_action(codec, res >> 26)) { - case HP_EVENT: - cs_automute(codec); - break; - case MIC_EVENT: - cs_automic(codec); - break; - } - snd_hda_jack_report_sync(codec); -} - -static const struct hda_codec_ops cs_patch_ops = { - .build_controls = cs_build_controls, - .build_pcms = cs_build_pcms, - .init = cs_init, - .free = cs_free, - .unsol_event = cs_unsol_event, -}; - -static int cs_parse_auto_config(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - int err; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - - err = parse_output(codec); - if (err < 0) - return err; - err = parse_input(codec); - if (err < 0) - return err; - err = parse_digital_output(codec); - if (err < 0) - return err; - err = parse_digital_input(codec); - if (err < 0) - return err; - return 0; -} - -static const char * const cs420x_models[CS420X_MODELS] = { - [CS420X_MBP53] = "mbp53", - [CS420X_MBP55] = "mbp55", - [CS420X_IMAC27] = "imac27", - [CS420X_IMAC27_122] = "imac27_122", - [CS420X_APPLE] = "apple", - [CS420X_AUTO] = "auto", -}; - - -static const struct snd_pci_quirk cs420x_cfg_tbl[] = { - SND_PCI_QUIRK(0x10de, 0x0ac0, "MacBookPro 5,3", CS420X_MBP53), - SND_PCI_QUIRK(0x10de, 0x0d94, "MacBookAir 3,1(2)", CS420X_MBP55), - SND_PCI_QUIRK(0x10de, 0xcb79, "MacBookPro 5,5", CS420X_MBP55), - SND_PCI_QUIRK(0x10de, 0xcb89, "MacBookPro 7,1", CS420X_MBP55), - /* this conflicts with too many other models */ - /*SND_PCI_QUIRK(0x8086, 0x7270, "IMac 27 Inch", CS420X_IMAC27),*/ - {} /* terminator */ -}; - -static const struct snd_pci_quirk cs420x_codec_cfg_tbl[] = { - SND_PCI_QUIRK(0x106b, 0x2000, "iMac 12,2", CS420X_IMAC27_122), - SND_PCI_QUIRK_VENDOR(0x106b, "Apple", CS420X_APPLE), - {} /* terminator */ -}; - -struct cs_pincfg { - hda_nid_t nid; - u32 val; -}; - -static const struct cs_pincfg mbp53_pincfgs[] = { - { 0x09, 0x012b4050 }, - { 0x0a, 0x90100141 }, - { 0x0b, 0x90100140 }, - { 0x0c, 0x018b3020 }, - { 0x0d, 0x90a00110 }, - { 0x0e, 0x400000f0 }, - { 0x0f, 0x01cbe030 }, - { 0x10, 0x014be060 }, - { 0x12, 0x400000f0 }, - { 0x15, 0x400000f0 }, - {} /* terminator */ -}; - -static const struct cs_pincfg mbp55_pincfgs[] = { - { 0x09, 0x012b4030 }, - { 0x0a, 0x90100121 }, - { 0x0b, 0x90100120 }, - { 0x0c, 0x400000f0 }, - { 0x0d, 0x90a00110 }, - { 0x0e, 0x400000f0 }, - { 0x0f, 0x400000f0 }, - { 0x10, 0x014be040 }, - { 0x12, 0x400000f0 }, - { 0x15, 0x400000f0 }, - {} /* terminator */ -}; - -static const struct cs_pincfg imac27_pincfgs[] = { - { 0x09, 0x012b4050 }, - { 0x0a, 0x90100140 }, - { 0x0b, 0x90100142 }, - { 0x0c, 0x018b3020 }, - { 0x0d, 0x90a00110 }, - { 0x0e, 0x400000f0 }, - { 0x0f, 0x01cbe030 }, - { 0x10, 0x014be060 }, - { 0x12, 0x01ab9070 }, - { 0x15, 0x400000f0 }, - {} /* terminator */ -}; - -static const struct cs_pincfg *cs_pincfgs[CS420X_MODELS] = { - [CS420X_MBP53] = mbp53_pincfgs, - [CS420X_MBP55] = mbp55_pincfgs, - [CS420X_IMAC27] = imac27_pincfgs, -}; - -static void fix_pincfg(struct hda_codec *codec, int model, - const struct cs_pincfg **pin_configs) -{ - const struct cs_pincfg *cfg = pin_configs[model]; - if (!cfg) - return; - for (; cfg->nid; cfg++) - snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val); -} - -static int patch_cs420x(struct hda_codec *codec) -{ - struct cs_spec *spec; - int err; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return -ENOMEM; - codec->spec = spec; - - spec->vendor_nid = CS420X_VENDOR_NID; - - spec->board_config = - snd_hda_check_board_config(codec, CS420X_MODELS, - cs420x_models, cs420x_cfg_tbl); - if (spec->board_config < 0) - spec->board_config = - snd_hda_check_board_codec_sid_config(codec, - CS420X_MODELS, NULL, cs420x_codec_cfg_tbl); - if (spec->board_config >= 0) - fix_pincfg(codec, spec->board_config, cs_pincfgs); - - switch (spec->board_config) { - case CS420X_IMAC27: - case CS420X_MBP53: - case CS420X_MBP55: - case CS420X_APPLE: - spec->gpio_eapd_hp = 2; /* GPIO1 = headphones */ - spec->gpio_eapd_speaker = 8; /* GPIO3 = speakers */ - spec->gpio_mask = spec->gpio_dir = - spec->gpio_eapd_hp | spec->gpio_eapd_speaker; - break; - case CS420X_IMAC27_122: - spec->gpio_eapd_hp = 4; /* GPIO2 = headphones */ - spec->gpio_eapd_speaker = 8; /* GPIO3 = speakers */ - spec->gpio_mask = spec->gpio_dir = - spec->gpio_eapd_hp | spec->gpio_eapd_speaker; - break; - } - - err = cs_parse_auto_config(codec); - if (err < 0) - goto error; - - codec->patch_ops = cs_patch_ops; - - return 0; - - error: - kfree(codec->spec); - codec->spec = NULL; - return err; -} - -/* - * Cirrus Logic CS4210 - * - * 1 DAC => HP(sense) / Speakers, - * 1 ADC <= LineIn(sense) / MicIn / DMicIn, - * 1 SPDIF OUT => SPDIF Trasmitter(sense) -*/ - -/* CS4210 board names */ -static const char *cs421x_models[CS421X_MODELS] = { - [CS421X_CDB4210] = "cdb4210", -}; - -static const struct snd_pci_quirk cs421x_cfg_tbl[] = { - /* Test Intel board + CDB2410 */ - SND_PCI_QUIRK(0x8086, 0x5001, "DP45SG/CDB4210", CS421X_CDB4210), - {} /* terminator */ -}; - -/* CS4210 board pinconfigs */ -/* Default CS4210 (CDB4210)*/ -static const struct cs_pincfg cdb4210_pincfgs[] = { - { 0x05, 0x0321401f }, - { 0x06, 0x90170010 }, - { 0x07, 0x03813031 }, - { 0x08, 0xb7a70037 }, - { 0x09, 0xb7a6003e }, - { 0x0a, 0x034510f0 }, - {} /* terminator */ -}; - -static const struct cs_pincfg *cs421x_pincfgs[CS421X_MODELS] = { - [CS421X_CDB4210] = cdb4210_pincfgs, -}; - -static const struct hda_verb cs421x_coef_init_verbs[] = { - {0x0B, AC_VERB_SET_PROC_STATE, 1}, - {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_DEV_CFG}, - /* - Disable Coefficient Index Auto-Increment(DAI)=1, - PDREF=0 - */ - {0x0B, AC_VERB_SET_PROC_COEF, 0x0001 }, - - {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_ADC_CFG}, - /* ADC SZCMode = Digital Soft Ramp */ - {0x0B, AC_VERB_SET_PROC_COEF, 0x0002 }, - - {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_DAC_CFG}, - {0x0B, AC_VERB_SET_PROC_COEF, - (0x0002 /* DAC SZCMode = Digital Soft Ramp */ - | 0x0004 /* Mute DAC on FIFO error */ - | 0x0008 /* Enable DAC High Pass Filter */ - )}, - {} /* terminator */ -}; - -/* Errata: CS4210 rev A1 Silicon - * - * http://www.cirrus.com/en/pubs/errata/ - * - * Description: - * 1. Performance degredation is present in the ADC. - * 2. Speaker output is not completely muted upon HP detect. - * 3. Noise is present when clipping occurs on the amplified - * speaker outputs. - * - * Workaround: - * The following verb sequence written to the registers during - * initialization will correct the issues listed above. - */ - -static const struct hda_verb cs421x_coef_init_verbs_A1_silicon_fixes[] = { - {0x0B, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */ - - {0x0B, AC_VERB_SET_COEF_INDEX, 0x0006}, - {0x0B, AC_VERB_SET_PROC_COEF, 0x9999}, /* Test mode: on */ - - {0x0B, AC_VERB_SET_COEF_INDEX, 0x000A}, - {0x0B, AC_VERB_SET_PROC_COEF, 0x14CB}, /* Chop double */ - - {0x0B, AC_VERB_SET_COEF_INDEX, 0x0011}, - {0x0B, AC_VERB_SET_PROC_COEF, 0xA2D0}, /* Increase ADC current */ - - {0x0B, AC_VERB_SET_COEF_INDEX, 0x001A}, - {0x0B, AC_VERB_SET_PROC_COEF, 0x02A9}, /* Mute speaker */ - - {0x0B, AC_VERB_SET_COEF_INDEX, 0x001B}, - {0x0B, AC_VERB_SET_PROC_COEF, 0X1006}, /* Remove noise */ - - {} /* terminator */ -}; - -/* Speaker Amp Gain is controlled by the vendor widget's coef 4 */ -static const DECLARE_TLV_DB_SCALE(cs421x_speaker_boost_db_scale, 900, 300, 0); - -static int cs421x_boost_vol_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 3; - return 0; -} - -static int cs421x_boost_vol_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - - ucontrol->value.integer.value[0] = - cs_vendor_coef_get(codec, CS421X_IDX_SPK_CTL) & 0x0003; - return 0; -} - -static int cs421x_boost_vol_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - - unsigned int vol = ucontrol->value.integer.value[0]; - unsigned int coef = - cs_vendor_coef_get(codec, CS421X_IDX_SPK_CTL); - unsigned int original_coef = coef; - - coef &= ~0x0003; - coef |= (vol & 0x0003); - if (original_coef == coef) - return 0; - else { - cs_vendor_coef_set(codec, CS421X_IDX_SPK_CTL, coef); - return 1; - } -} - -static const struct snd_kcontrol_new cs421x_speaker_bost_ctl = { - - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | - SNDRV_CTL_ELEM_ACCESS_TLV_READ), - .name = "Speaker Boost Playback Volume", - .info = cs421x_boost_vol_info, - .get = cs421x_boost_vol_get, - .put = cs421x_boost_vol_put, - .tlv = { .p = cs421x_speaker_boost_db_scale }, -}; - -static void cs4210_pinmux_init(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - unsigned int def_conf, coef; - - /* GPIO, DMIC_SCL, DMIC_SDA and SENSE_B are multiplexed */ - coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG); - - if (spec->gpio_mask) - coef |= 0x0008; /* B1,B2 are GPIOs */ - else - coef &= ~0x0008; - - if (spec->sense_b) - coef |= 0x0010; /* B2 is SENSE_B, not inverted */ - else - coef &= ~0x0010; - - cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef); - - if ((spec->gpio_mask || spec->sense_b) && - is_active_pin(codec, CS421X_DMIC_PIN_NID)) { - - /* - GPIO or SENSE_B forced - disconnect the DMIC pin. - */ - def_conf = snd_hda_codec_get_pincfg(codec, CS421X_DMIC_PIN_NID); - def_conf &= ~AC_DEFCFG_PORT_CONN; - def_conf |= (AC_JACK_PORT_NONE << AC_DEFCFG_PORT_CONN_SHIFT); - snd_hda_codec_set_pincfg(codec, CS421X_DMIC_PIN_NID, def_conf); - } -} - -static void init_cs421x_digital(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - - for (i = 0; i < cfg->dig_outs; i++) { - hda_nid_t nid = cfg->dig_out_pins[i]; - if (!cfg->speaker_outs) - continue; - if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) { - snd_hda_jack_detect_enable(codec, nid, SPDIF_EVENT); - spec->spdif_detect = 1; - } - } -} - -static int cs421x_init(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - - if (spec->vendor_nid == CS4210_VENDOR_NID) { - snd_hda_sequence_write(codec, cs421x_coef_init_verbs); - snd_hda_sequence_write(codec, cs421x_coef_init_verbs_A1_silicon_fixes); - cs4210_pinmux_init(codec); - } - - if (spec->gpio_mask) { - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, - spec->gpio_mask); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, - spec->gpio_dir); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_data); - } - - init_output(codec); - init_input(codec); - init_cs421x_digital(codec); - snd_hda_jack_report_sync(codec); - - return 0; -} - -/* - * CS4210 Input MUX (1 ADC) - */ -static int cs421x_mux_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct cs_spec *spec = codec->spec; - - return snd_hda_input_mux_info(&spec->input_mux, uinfo); -} - -static int cs421x_mux_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct cs_spec *spec = codec->spec; - - ucontrol->value.enumerated.item[0] = spec->cur_input; - return 0; -} - -static int cs421x_mux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct cs_spec *spec = codec->spec; - - return snd_hda_input_mux_put(codec, &spec->input_mux, ucontrol, - spec->adc_nid[0], &spec->cur_input); - -} - -static struct snd_kcontrol_new cs421x_capture_source = { - - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, - .info = cs421x_mux_enum_info, - .get = cs421x_mux_enum_get, - .put = cs421x_mux_enum_put, -}; - -static int cs421x_add_input_volume_control(struct hda_codec *codec, int item) -{ - struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - const struct hda_input_mux *imux = &spec->input_mux; - hda_nid_t pin = cfg->inputs[item].pin; - struct snd_kcontrol *kctl; - u32 caps; - - if (!(get_wcaps(codec, pin) & AC_WCAP_IN_AMP)) - return 0; - - caps = query_amp_caps(codec, pin, HDA_INPUT); - caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; - if (caps <= 1) - return 0; - - return add_volume(codec, imux->items[item].label, 0, - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT), 1, &kctl); -} - -/* add a (input-boost) volume control to the given input pin */ -static int build_cs421x_input(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - struct hda_input_mux *imux = &spec->input_mux; - int i, err, type_idx; - const char *label; - - if (!spec->num_inputs) - return 0; - - /* make bind-capture */ - spec->capture_bind[0] = make_bind_capture(codec, &snd_hda_bind_sw); - spec->capture_bind[1] = make_bind_capture(codec, &snd_hda_bind_vol); - for (i = 0; i < 2; i++) { - struct snd_kcontrol *kctl; - int n; - if (!spec->capture_bind[i]) - return -ENOMEM; - kctl = snd_ctl_new1(&cs_capture_ctls[i], codec); - if (!kctl) - return -ENOMEM; - kctl->private_value = (long)spec->capture_bind[i]; - err = snd_hda_ctl_add(codec, 0, kctl); - if (err < 0) - return err; - for (n = 0; n < AUTO_PIN_LAST; n++) { - if (!spec->adc_nid[n]) - continue; - err = snd_hda_add_nid(codec, kctl, 0, spec->adc_nid[n]); - if (err < 0) - return err; - } - } - - /* Add Input MUX Items + Capture Volume/Switch */ - for (i = 0; i < spec->num_inputs; i++) { - label = hda_get_autocfg_input_label(codec, cfg, i); - snd_hda_add_imux_item(imux, label, spec->adc_idx[i], &type_idx); - - err = cs421x_add_input_volume_control(codec, i); - if (err < 0) - return err; - } - - /* - Add 'Capture Source' Switch if - * 2 inputs and no mic detec - * 3 inputs - */ - if ((spec->num_inputs == 2 && !spec->mic_detect) || - (spec->num_inputs == 3)) { - - err = snd_hda_ctl_add(codec, spec->adc_nid[0], - snd_ctl_new1(&cs421x_capture_source, codec)); - if (err < 0) - return err; - } - - return 0; -} - -/* Single DAC (Mute/Gain) */ -static int build_cs421x_output(struct hda_codec *codec) -{ - hda_nid_t dac = CS4210_DAC_NID; - struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - struct snd_kcontrol *kctl; - int err; - char *name = "Master"; - - fix_volume_caps(codec, dac); - - err = add_mute(codec, name, 0, - HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl); - if (err < 0) - return err; - - err = add_volume(codec, name, 0, - HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl); - if (err < 0) - return err; - - if (cfg->speaker_outs && (spec->vendor_nid == CS4210_VENDOR_NID)) { - err = snd_hda_ctl_add(codec, 0, - snd_ctl_new1(&cs421x_speaker_bost_ctl, codec)); - if (err < 0) - return err; - } - return err; -} - -static int cs421x_build_controls(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - int err; - - err = build_cs421x_output(codec); - if (err < 0) - return err; - err = build_cs421x_input(codec); - if (err < 0) - return err; - err = build_digital_output(codec); - if (err < 0) - return err; - err = cs421x_init(codec); - if (err < 0) - return err; - - err = snd_hda_jack_add_kctls(codec, &spec->autocfg); - if (err < 0) - return err; - - return 0; -} - -static void cs421x_unsol_event(struct hda_codec *codec, unsigned int res) -{ - switch (snd_hda_jack_get_action(codec, res >> 26)) { - case HP_EVENT: - case SPDIF_EVENT: - cs_automute(codec); - break; - - case MIC_EVENT: - cs_automic(codec); - break; - } - snd_hda_jack_report_sync(codec); -} - -static int parse_cs421x_input(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t pin = cfg->inputs[i].pin; - spec->adc_nid[i] = get_adc(codec, pin, &spec->adc_idx[i]); - spec->cur_input = spec->last_input = i; - spec->num_inputs++; - - /* check whether the automatic mic switch is available */ - if (is_ext_mic(codec, i) && cfg->num_inputs >= 2) { - spec->mic_detect = 1; - spec->automic_idx = i; - } - } - return 0; -} - -static int cs421x_parse_auto_config(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - int err; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - err = parse_output(codec); - if (err < 0) - return err; - err = parse_cs421x_input(codec); - if (err < 0) - return err; - err = parse_digital_output(codec); - if (err < 0) - return err; - return 0; -} - -#ifdef CONFIG_PM -/* - Manage PDREF, when transitioning to D3hot - (DAC,ADC) -> D3, PDREF=1, AFG->D3 -*/ -static int cs421x_suspend(struct hda_codec *codec, pm_message_t state) -{ - struct cs_spec *spec = codec->spec; - unsigned int coef; - - snd_hda_shutup_pins(codec); - - snd_hda_codec_write(codec, CS4210_DAC_NID, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); - snd_hda_codec_write(codec, CS4210_ADC_NID, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); - - if (spec->vendor_nid == CS4210_VENDOR_NID) { - coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG); - coef |= 0x0004; /* PDREF */ - cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef); - } - - return 0; -} -#endif - -static struct hda_codec_ops cs421x_patch_ops = { - .build_controls = cs421x_build_controls, - .build_pcms = cs_build_pcms, - .init = cs421x_init, - .free = cs_free, - .unsol_event = cs421x_unsol_event, -#ifdef CONFIG_PM - .suspend = cs421x_suspend, -#endif -}; - -static int patch_cs4210(struct hda_codec *codec) -{ - struct cs_spec *spec; - int err; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return -ENOMEM; - codec->spec = spec; - - spec->vendor_nid = CS4210_VENDOR_NID; - - spec->board_config = - snd_hda_check_board_config(codec, CS421X_MODELS, - cs421x_models, cs421x_cfg_tbl); - if (spec->board_config >= 0) - fix_pincfg(codec, spec->board_config, cs421x_pincfgs); - /* - Setup GPIO/SENSE for each board (if used) - */ - switch (spec->board_config) { - case CS421X_CDB4210: - snd_printd("CS4210 board: %s\n", - cs421x_models[spec->board_config]); -/* spec->gpio_mask = 3; - spec->gpio_dir = 3; - spec->gpio_data = 3; -*/ - spec->sense_b = 1; - - break; - } - - /* - Update the GPIO/DMIC/SENSE_B pinmux before the configuration - is auto-parsed. If GPIO or SENSE_B is forced, DMIC input - is disabled. - */ - cs4210_pinmux_init(codec); - - err = cs421x_parse_auto_config(codec); - if (err < 0) - goto error; - - codec->patch_ops = cs421x_patch_ops; - - return 0; - - error: - kfree(codec->spec); - codec->spec = NULL; - return err; -} - -static int patch_cs4213(struct hda_codec *codec) -{ - struct cs_spec *spec; - int err; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return -ENOMEM; - codec->spec = spec; - - spec->vendor_nid = CS4213_VENDOR_NID; - - err = cs421x_parse_auto_config(codec); - if (err < 0) - goto error; - - codec->patch_ops = cs421x_patch_ops; - return 0; - - error: - kfree(codec->spec); - codec->spec = NULL; - return err; -} - - -/* - * patch entries - */ -static const struct hda_codec_preset snd_hda_preset_cirrus[] = { - { .id = 0x10134206, .name = "CS4206", .patch = patch_cs420x }, - { .id = 0x10134207, .name = "CS4207", .patch = patch_cs420x }, - { .id = 0x10134210, .name = "CS4210", .patch = patch_cs4210 }, - { .id = 0x10134213, .name = "CS4213", .patch = patch_cs4213 }, - {} /* terminator */ -}; - -MODULE_ALIAS("snd-hda-codec-id:10134206"); -MODULE_ALIAS("snd-hda-codec-id:10134207"); -MODULE_ALIAS("snd-hda-codec-id:10134210"); -MODULE_ALIAS("snd-hda-codec-id:10134213"); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Cirrus Logic HD-audio codec"); - -static struct hda_codec_preset_list cirrus_list = { - .preset = snd_hda_preset_cirrus, - .owner = THIS_MODULE, -}; - -static int __init patch_cirrus_init(void) -{ - return snd_hda_add_codec_preset(&cirrus_list); -} - -static void __exit patch_cirrus_exit(void) -{ - snd_hda_delete_codec_preset(&cirrus_list); -} - -module_init(patch_cirrus_init) -module_exit(patch_cirrus_exit) diff --git a/ANDROID_3.4.5/sound/pci/hda/patch_cmedia.c b/ANDROID_3.4.5/sound/pci/hda/patch_cmedia.c deleted file mode 100644 index b6767b4c..00000000 --- a/ANDROID_3.4.5/sound/pci/hda/patch_cmedia.c +++ /dev/null @@ -1,776 +0,0 @@ -/* - * Universal Interface for Intel High Definition Audio Codec - * - * HD audio interface patch for C-Media CMI9880 - * - * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> - * - * - * 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 <linux/delay.h> -#include <linux/slab.h> -#include <linux/pci.h> -#include <linux/module.h> -#include <sound/core.h> -#include "hda_codec.h" -#include "hda_local.h" -#define NUM_PINS 11 - - -/* board config type */ -enum { - CMI_MINIMAL, /* back 3-jack */ - CMI_MIN_FP, /* back 3-jack + front-panel 2-jack */ - CMI_FULL, /* back 6-jack + front-panel 2-jack */ - CMI_FULL_DIG, /* back 6-jack + front-panel 2-jack + digital I/O */ - CMI_ALLOUT, /* back 5-jack + front-panel 2-jack + digital out */ - CMI_AUTO, /* let driver guess it */ - CMI_MODELS -}; - -struct cmi_spec { - int board_config; - unsigned int no_line_in: 1; /* no line-in (5-jack) */ - unsigned int front_panel: 1; /* has front-panel 2-jack */ - - /* playback */ - struct hda_multi_out multiout; - hda_nid_t dac_nids[AUTO_CFG_MAX_OUTS]; /* NID for each DAC */ - int num_dacs; - - /* capture */ - const hda_nid_t *adc_nids; - hda_nid_t dig_in_nid; - - /* capture source */ - const struct hda_input_mux *input_mux; - unsigned int cur_mux[2]; - - /* channel mode */ - int num_channel_modes; - const struct hda_channel_mode *channel_modes; - - struct hda_pcm pcm_rec[2]; /* PCM information */ - - /* pin default configuration */ - hda_nid_t pin_nid[NUM_PINS]; - unsigned int def_conf[NUM_PINS]; - unsigned int pin_def_confs; - - /* multichannel pins */ - struct hda_verb multi_init[9]; /* 2 verbs for each pin + terminator */ -}; - -/* - * input MUX - */ -static int cmi_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct cmi_spec *spec = codec->spec; - return snd_hda_input_mux_info(spec->input_mux, uinfo); -} - -static int cmi_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct cmi_spec *spec = codec->spec; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - - ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; - return 0; -} - -static int cmi_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct cmi_spec *spec = codec->spec; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - - return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, - spec->adc_nids[adc_idx], &spec->cur_mux[adc_idx]); -} - -/* - * shared line-in, mic for surrounds - */ - -/* 3-stack / 2 channel */ -static const struct hda_verb cmi9880_ch2_init[] = { - /* set line-in PIN for input */ - { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - /* set mic PIN for input, also enable vref */ - { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - /* route front PCM (DAC1) to HP */ - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, - {} -}; - -/* 3-stack / 6 channel */ -static const struct hda_verb cmi9880_ch6_init[] = { - /* set line-in PIN for output */ - { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - /* set mic PIN for output */ - { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - /* route front PCM (DAC1) to HP */ - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, - {} -}; - -/* 3-stack+front / 8 channel */ -static const struct hda_verb cmi9880_ch8_init[] = { - /* set line-in PIN for output */ - { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - /* set mic PIN for output */ - { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - /* route rear-surround PCM (DAC4) to HP */ - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x03 }, - {} -}; - -static const struct hda_channel_mode cmi9880_channel_modes[3] = { - { 2, cmi9880_ch2_init }, - { 6, cmi9880_ch6_init }, - { 8, cmi9880_ch8_init }, -}; - -static int cmi_ch_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct cmi_spec *spec = codec->spec; - return snd_hda_ch_mode_info(codec, uinfo, spec->channel_modes, - spec->num_channel_modes); -} - -static int cmi_ch_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct cmi_spec *spec = codec->spec; - return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_modes, - spec->num_channel_modes, spec->multiout.max_channels); -} - -static int cmi_ch_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct cmi_spec *spec = codec->spec; - return snd_hda_ch_mode_put(codec, ucontrol, spec->channel_modes, - spec->num_channel_modes, &spec->multiout.max_channels); -} - -/* - */ -static const struct snd_kcontrol_new cmi9880_basic_mixer[] = { - /* CMI9880 has no playback volumes! */ - HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT), /* front */ - HDA_CODEC_MUTE("Surround Playback Switch", 0x04, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Side Playback Switch", 0x06, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = cmi_mux_enum_info, - .get = cmi_mux_enum_get, - .put = cmi_mux_enum_put, - }, - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Beep Playback Volume", 0x23, 0, HDA_OUTPUT), - HDA_CODEC_MUTE("Beep Playback Switch", 0x23, 0, HDA_OUTPUT), - { } /* end */ -}; - -/* - * shared I/O pins - */ -static const struct snd_kcontrol_new cmi9880_ch_mode_mixer[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = cmi_ch_mode_info, - .get = cmi_ch_mode_get, - .put = cmi_ch_mode_put, - }, - { } /* end */ -}; - -/* AUD-in selections: - * 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x1f 0x20 - */ -static const struct hda_input_mux cmi9880_basic_mux = { - .num_items = 4, - .items = { - { "Front Mic", 0x5 }, - { "Rear Mic", 0x2 }, - { "Line", 0x1 }, - { "CD", 0x7 }, - } -}; - -static const struct hda_input_mux cmi9880_no_line_mux = { - .num_items = 3, - .items = { - { "Front Mic", 0x5 }, - { "Rear Mic", 0x2 }, - { "CD", 0x7 }, - } -}; - -/* front, rear, clfe, rear_surr */ -static const hda_nid_t cmi9880_dac_nids[4] = { - 0x03, 0x04, 0x05, 0x06 -}; -/* ADC0, ADC1 */ -static const hda_nid_t cmi9880_adc_nids[2] = { - 0x08, 0x09 -}; - -#define CMI_DIG_OUT_NID 0x07 -#define CMI_DIG_IN_NID 0x0a - -/* - */ -static const struct hda_verb cmi9880_basic_init[] = { - /* port-D for line out (rear panel) */ - { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, - /* port-E for HP out (front panel) */ - { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, - /* route front PCM to HP */ - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, - /* port-A for surround (rear panel) */ - { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, - /* port-G for CLFE (rear panel) */ - { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, - { 0x1f, AC_VERB_SET_CONNECT_SEL, 0x02 }, - /* port-H for side (rear panel) */ - { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, - { 0x20, AC_VERB_SET_CONNECT_SEL, 0x01 }, - /* port-C for line-in (rear panel) */ - { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - /* port-B for mic-in (rear panel) with vref */ - { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - /* port-F for mic-in (front panel) with vref */ - { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - /* CD-in */ - { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - /* route front mic to ADC1/2 */ - { 0x08, AC_VERB_SET_CONNECT_SEL, 0x05 }, - { 0x09, AC_VERB_SET_CONNECT_SEL, 0x05 }, - {} /* terminator */ -}; - -static const struct hda_verb cmi9880_allout_init[] = { - /* port-D for line out (rear panel) */ - { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, - /* port-E for HP out (front panel) */ - { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, - /* route front PCM to HP */ - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, - /* port-A for side (rear panel) */ - { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, - /* port-G for CLFE (rear panel) */ - { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, - { 0x1f, AC_VERB_SET_CONNECT_SEL, 0x02 }, - /* port-H for side (rear panel) */ - { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, - { 0x20, AC_VERB_SET_CONNECT_SEL, 0x01 }, - /* port-C for surround (rear panel) */ - { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, - /* port-B for mic-in (rear panel) with vref */ - { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - /* port-F for mic-in (front panel) with vref */ - { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - /* CD-in */ - { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - /* route front mic to ADC1/2 */ - { 0x08, AC_VERB_SET_CONNECT_SEL, 0x05 }, - { 0x09, AC_VERB_SET_CONNECT_SEL, 0x05 }, - {} /* terminator */ -}; - -/* - */ -static int cmi9880_build_controls(struct hda_codec *codec) -{ - struct cmi_spec *spec = codec->spec; - struct snd_kcontrol *kctl; - int i, err; - - err = snd_hda_add_new_ctls(codec, cmi9880_basic_mixer); - if (err < 0) - return err; - if (spec->channel_modes) { - err = snd_hda_add_new_ctls(codec, cmi9880_ch_mode_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, - spec->multiout.dig_out_nid); - if (err < 0) - return err; - err = snd_hda_create_spdif_share_sw(codec, - &spec->multiout); - if (err < 0) - return err; - spec->multiout.share_spdif = 1; - } - if (spec->dig_in_nid) { - err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); - if (err < 0) - return err; - } - - /* assign Capture Source enums to NID */ - kctl = snd_hda_find_mixer_ctl(codec, "Capture Source"); - for (i = 0; kctl && i < kctl->count; i++) { - err = snd_hda_add_nid(codec, kctl, i, spec->adc_nids[i]); - if (err < 0) - return err; - } - return 0; -} - -/* fill in the multi_dac_nids table, which will decide - which audio widget to use for each channel */ -static int cmi9880_fill_multi_dac_nids(struct hda_codec *codec, const struct auto_pin_cfg *cfg) -{ - struct cmi_spec *spec = codec->spec; - hda_nid_t nid; - int assigned[4]; - int i, j; - - /* clear the table, only one c-media dac assumed here */ - memset(spec->dac_nids, 0, sizeof(spec->dac_nids)); - memset(assigned, 0, sizeof(assigned)); - /* check the pins we found */ - for (i = 0; i < cfg->line_outs; i++) { - nid = cfg->line_out_pins[i]; - /* nid 0x0b~0x0e is hardwired to audio widget 0x3~0x6 */ - if (nid >= 0x0b && nid <= 0x0e) { - spec->dac_nids[i] = (nid - 0x0b) + 0x03; - assigned[nid - 0x0b] = 1; - } - } - /* left pin can be connect to any audio widget */ - for (i = 0; i < cfg->line_outs; i++) { - nid = cfg->line_out_pins[i]; - if (nid <= 0x0e) - continue; - /* search for an empty channel */ - for (j = 0; j < cfg->line_outs; j++) { - if (! assigned[j]) { - spec->dac_nids[i] = j + 0x03; - assigned[j] = 1; - break; - } - } - } - spec->num_dacs = cfg->line_outs; - return 0; -} - -/* create multi_init table, which is used for multichannel initialization */ -static int cmi9880_fill_multi_init(struct hda_codec *codec, const struct auto_pin_cfg *cfg) -{ - struct cmi_spec *spec = codec->spec; - hda_nid_t nid; - int i, j, k; - - /* clear the table, only one c-media dac assumed here */ - memset(spec->multi_init, 0, sizeof(spec->multi_init)); - for (j = 0, i = 0; i < cfg->line_outs; i++) { - nid = cfg->line_out_pins[i]; - /* set as output */ - spec->multi_init[j].nid = nid; - spec->multi_init[j].verb = AC_VERB_SET_PIN_WIDGET_CONTROL; - spec->multi_init[j].param = PIN_OUT; - j++; - if (nid > 0x0e) { - /* set connection */ - spec->multi_init[j].nid = nid; - spec->multi_init[j].verb = AC_VERB_SET_CONNECT_SEL; - spec->multi_init[j].param = 0; - /* find the index in connect list */ - k = snd_hda_get_conn_index(codec, nid, - spec->dac_nids[i], 0); - if (k >= 0) - spec->multi_init[j].param = k; - j++; - } - } - return 0; -} - -static int cmi9880_init(struct hda_codec *codec) -{ - struct cmi_spec *spec = codec->spec; - if (spec->board_config == CMI_ALLOUT) - snd_hda_sequence_write(codec, cmi9880_allout_init); - else - snd_hda_sequence_write(codec, cmi9880_basic_init); - if (spec->board_config == CMI_AUTO) - snd_hda_sequence_write(codec, spec->multi_init); - return 0; -} - -/* - * Analog playback callbacks - */ -static int cmi9880_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct cmi_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, - hinfo); -} - -static int cmi9880_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 cmi_spec *spec = codec->spec; - return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, - format, substream); -} - -static int cmi9880_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct cmi_spec *spec = codec->spec; - return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); -} - -/* - * Digital out - */ -static int cmi9880_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct cmi_spec *spec = codec->spec; - return snd_hda_multi_out_dig_open(codec, &spec->multiout); -} - -static int cmi9880_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct cmi_spec *spec = codec->spec; - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} - -static int cmi9880_dig_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 cmi_spec *spec = codec->spec; - return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, - format, substream); -} - -/* - * Analog capture - */ -static int cmi9880_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct cmi_spec *spec = codec->spec; - - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], - stream_tag, 0, format); - return 0; -} - -static int cmi9880_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct cmi_spec *spec = codec->spec; - - snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); - return 0; -} - - -/* - */ -static const struct hda_pcm_stream cmi9880_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 8, - .nid = 0x03, /* NID to query formats and rates */ - .ops = { - .open = cmi9880_playback_pcm_open, - .prepare = cmi9880_playback_pcm_prepare, - .cleanup = cmi9880_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream cmi9880_pcm_analog_capture = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x08, /* NID to query formats and rates */ - .ops = { - .prepare = cmi9880_capture_pcm_prepare, - .cleanup = cmi9880_capture_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream cmi9880_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in cmi9880_build_pcms */ - .ops = { - .open = cmi9880_dig_playback_pcm_open, - .close = cmi9880_dig_playback_pcm_close, - .prepare = cmi9880_dig_playback_pcm_prepare - }, -}; - -static const struct hda_pcm_stream cmi9880_pcm_digital_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in cmi9880_build_pcms */ -}; - -static int cmi9880_build_pcms(struct hda_codec *codec) -{ - struct cmi_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; - - codec->num_pcms = 1; - codec->pcm_info = info; - - info->name = "CMI9880"; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = cmi9880_pcm_analog_playback; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = cmi9880_pcm_analog_capture; - - if (spec->multiout.dig_out_nid || spec->dig_in_nid) { - codec->num_pcms++; - info++; - info->name = "CMI9880 Digital"; - info->pcm_type = HDA_PCM_TYPE_SPDIF; - if (spec->multiout.dig_out_nid) { - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = cmi9880_pcm_digital_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; - } - if (spec->dig_in_nid) { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = cmi9880_pcm_digital_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; - } - } - - return 0; -} - -static void cmi9880_free(struct hda_codec *codec) -{ - kfree(codec->spec); -} - -/* - */ - -static const char * const cmi9880_models[CMI_MODELS] = { - [CMI_MINIMAL] = "minimal", - [CMI_MIN_FP] = "min_fp", - [CMI_FULL] = "full", - [CMI_FULL_DIG] = "full_dig", - [CMI_ALLOUT] = "allout", - [CMI_AUTO] = "auto", -}; - -static const struct snd_pci_quirk cmi9880_cfg_tbl[] = { - SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", CMI_FULL_DIG), - SND_PCI_QUIRK(0x1854, 0x002b, "LG LS75", CMI_MINIMAL), - SND_PCI_QUIRK(0x1854, 0x0032, "LG", CMI_FULL_DIG), - {} /* terminator */ -}; - -static const struct hda_codec_ops cmi9880_patch_ops = { - .build_controls = cmi9880_build_controls, - .build_pcms = cmi9880_build_pcms, - .init = cmi9880_init, - .free = cmi9880_free, -}; - -static int patch_cmi9880(struct hda_codec *codec) -{ - struct cmi_spec *spec; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - spec->board_config = snd_hda_check_board_config(codec, CMI_MODELS, - cmi9880_models, - cmi9880_cfg_tbl); - if (spec->board_config < 0) { - snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - spec->board_config = CMI_AUTO; /* try everything */ - } - - /* copy default DAC NIDs */ - memcpy(spec->dac_nids, cmi9880_dac_nids, sizeof(spec->dac_nids)); - spec->num_dacs = 4; - - switch (spec->board_config) { - case CMI_MINIMAL: - case CMI_MIN_FP: - spec->channel_modes = cmi9880_channel_modes; - if (spec->board_config == CMI_MINIMAL) - spec->num_channel_modes = 2; - else { - spec->front_panel = 1; - spec->num_channel_modes = 3; - } - spec->multiout.max_channels = cmi9880_channel_modes[0].channels; - spec->input_mux = &cmi9880_basic_mux; - break; - case CMI_FULL: - case CMI_FULL_DIG: - spec->front_panel = 1; - spec->multiout.max_channels = 8; - spec->input_mux = &cmi9880_basic_mux; - if (spec->board_config == CMI_FULL_DIG) { - spec->multiout.dig_out_nid = CMI_DIG_OUT_NID; - spec->dig_in_nid = CMI_DIG_IN_NID; - } - break; - case CMI_ALLOUT: - spec->front_panel = 1; - spec->multiout.max_channels = 8; - spec->no_line_in = 1; - spec->input_mux = &cmi9880_no_line_mux; - spec->multiout.dig_out_nid = CMI_DIG_OUT_NID; - break; - case CMI_AUTO: - { - unsigned int port_e, port_f, port_g, port_h; - unsigned int port_spdifi, port_spdifo; - struct auto_pin_cfg cfg; - - /* collect pin default configuration */ - port_e = snd_hda_codec_get_pincfg(codec, 0x0f); - port_f = snd_hda_codec_get_pincfg(codec, 0x10); - spec->front_panel = 1; - if (get_defcfg_connect(port_e) == AC_JACK_PORT_NONE || - get_defcfg_connect(port_f) == AC_JACK_PORT_NONE) { - port_g = snd_hda_codec_get_pincfg(codec, 0x1f); - port_h = snd_hda_codec_get_pincfg(codec, 0x20); - spec->channel_modes = cmi9880_channel_modes; - /* no front panel */ - if (get_defcfg_connect(port_g) == AC_JACK_PORT_NONE || - get_defcfg_connect(port_h) == AC_JACK_PORT_NONE) { - /* no optional rear panel */ - spec->board_config = CMI_MINIMAL; - spec->front_panel = 0; - spec->num_channel_modes = 2; - } else { - spec->board_config = CMI_MIN_FP; - spec->num_channel_modes = 3; - } - spec->input_mux = &cmi9880_basic_mux; - spec->multiout.max_channels = cmi9880_channel_modes[0].channels; - } else { - spec->input_mux = &cmi9880_basic_mux; - port_spdifi = snd_hda_codec_get_pincfg(codec, 0x13); - port_spdifo = snd_hda_codec_get_pincfg(codec, 0x12); - if (get_defcfg_connect(port_spdifo) != AC_JACK_PORT_NONE) - spec->multiout.dig_out_nid = CMI_DIG_OUT_NID; - if (get_defcfg_connect(port_spdifi) != AC_JACK_PORT_NONE) - spec->dig_in_nid = CMI_DIG_IN_NID; - spec->multiout.max_channels = 8; - } - snd_hda_parse_pin_def_config(codec, &cfg, NULL); - if (cfg.line_outs) { - spec->multiout.max_channels = cfg.line_outs * 2; - cmi9880_fill_multi_dac_nids(codec, &cfg); - cmi9880_fill_multi_init(codec, &cfg); - } else - snd_printd("patch_cmedia: cannot detect association in defcfg\n"); - break; - } - } - - spec->multiout.num_dacs = spec->num_dacs; - spec->multiout.dac_nids = spec->dac_nids; - - spec->adc_nids = cmi9880_adc_nids; - - codec->patch_ops = cmi9880_patch_ops; - - return 0; -} - -/* - * patch entries - */ -static const 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/ANDROID_3.4.5/sound/pci/hda/patch_conexant.c b/ANDROID_3.4.5/sound/pci/hda/patch_conexant.c deleted file mode 100644 index d906c5b7..00000000 --- a/ANDROID_3.4.5/sound/pci/hda/patch_conexant.c +++ /dev/null @@ -1,4599 +0,0 @@ -/* - * HD audio interface patch for Conexant HDA audio codec - * - * Copyright (c) 2006 Pototskiy Akex <alex.pototskiy@gmail.com> - * Takashi Iwai <tiwai@suse.de> - * Tobin Davis <tdavis@dsl-only.net> - * - * 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 <linux/delay.h> -#include <linux/slab.h> -#include <linux/pci.h> -#include <linux/module.h> -#include <sound/core.h> -#include <sound/jack.h> - -#include "hda_codec.h" -#include "hda_local.h" -#include "hda_beep.h" -#include "hda_jack.h" - -#define CXT_PIN_DIR_IN 0x00 -#define CXT_PIN_DIR_OUT 0x01 -#define CXT_PIN_DIR_INOUT 0x02 -#define CXT_PIN_DIR_IN_NOMICBIAS 0x03 -#define CXT_PIN_DIR_INOUT_NOMICBIAS 0x04 - -#define CONEXANT_HP_EVENT 0x37 -#define CONEXANT_MIC_EVENT 0x38 -#define CONEXANT_LINE_EVENT 0x39 - -/* Conexant 5051 specific */ - -#define CXT5051_SPDIF_OUT 0x12 -#define CXT5051_PORTB_EVENT 0x38 -#define CXT5051_PORTC_EVENT 0x39 - -#define AUTO_MIC_PORTB (1 << 1) -#define AUTO_MIC_PORTC (1 << 2) - -struct pin_dac_pair { - hda_nid_t pin; - hda_nid_t dac; - int type; -}; - -struct imux_info { - hda_nid_t pin; /* input pin NID */ - hda_nid_t adc; /* connected ADC NID */ - hda_nid_t boost; /* optional boost volume NID */ - int index; /* corresponding to autocfg.input */ -}; - -struct conexant_spec { - - const struct snd_kcontrol_new *mixers[5]; - int num_mixers; - hda_nid_t vmaster_nid; - struct hda_vmaster_mute_hook vmaster_mute; - bool vmaster_mute_led; - - const struct hda_verb *init_verbs[5]; /* initialization verbs - * don't forget NULL - * termination! - */ - unsigned int num_init_verbs; - - /* playback */ - struct hda_multi_out multiout; /* playback set-up - * max_channels, dacs must be set - * dig_out_nid and hp_nid are optional - */ - unsigned int cur_eapd; - unsigned int hp_present; - unsigned int line_present; - unsigned int auto_mic; - int auto_mic_ext; /* imux_pins[] index for ext mic */ - int auto_mic_dock; /* imux_pins[] index for dock mic */ - int auto_mic_int; /* imux_pins[] index for int mic */ - unsigned int need_dac_fix; - hda_nid_t slave_dig_outs[2]; - - /* capture */ - unsigned int num_adc_nids; - const hda_nid_t *adc_nids; - hda_nid_t dig_in_nid; /* digital-in NID; optional */ - - unsigned int cur_adc_idx; - hda_nid_t cur_adc; - unsigned int cur_adc_stream_tag; - unsigned int cur_adc_format; - - const struct hda_pcm_stream *capture_stream; - - /* capture source */ - const struct hda_input_mux *input_mux; - const hda_nid_t *capsrc_nids; - unsigned int cur_mux[3]; - - /* channel model */ - const struct hda_channel_mode *channel_mode; - int num_channel_mode; - - /* PCM information */ - struct hda_pcm pcm_rec[2]; /* used in build_pcms() */ - - unsigned int spdif_route; - - /* dynamic controls, init_verbs and input_mux */ - struct auto_pin_cfg autocfg; - struct hda_input_mux private_imux; - struct imux_info imux_info[HDA_MAX_NUM_INPUTS]; - hda_nid_t private_adc_nids[HDA_MAX_NUM_INPUTS]; - hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; - struct pin_dac_pair dac_info[8]; - int dac_info_filled; - - unsigned int port_d_mode; - unsigned int auto_mute:1; /* used in auto-parser */ - unsigned int detect_line:1; /* Line-out detection enabled */ - unsigned int automute_lines:1; /* automute line-out as well */ - unsigned int automute_hp_lo:1; /* both HP and LO available */ - unsigned int dell_automute:1; - unsigned int dell_vostro:1; - unsigned int ideapad:1; - unsigned int thinkpad:1; - unsigned int hp_laptop:1; - unsigned int asus:1; - unsigned int pin_eapd_ctrls:1; - - unsigned int adc_switching:1; - - unsigned int ext_mic_present; - unsigned int recording; - void (*capture_prepare)(struct hda_codec *codec); - void (*capture_cleanup)(struct hda_codec *codec); - - /* OLPC XO-1.5 supports DC input mode (e.g. for use with analog sensors) - * through the microphone jack. - * When the user enables this through a mixer switch, both internal and - * external microphones are disabled. Gain is fixed at 0dB. In this mode, - * we also allow the bias to be configured through a separate mixer - * control. */ - unsigned int dc_enable; - unsigned int dc_input_bias; /* offset into cxt5066_olpc_dc_bias */ - unsigned int mic_boost; /* offset into cxt5066_analog_mic_boost */ - - unsigned int beep_amp; - - /* extra EAPD pins */ - unsigned int num_eapds; - hda_nid_t eapds[4]; -}; - -static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct conexant_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, - hinfo); -} - -static int conexant_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 conexant_spec *spec = codec->spec; - return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, - stream_tag, - format, substream); -} - -static int conexant_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct conexant_spec *spec = codec->spec; - return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); -} - -/* - * Digital out - */ -static int conexant_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct conexant_spec *spec = codec->spec; - return snd_hda_multi_out_dig_open(codec, &spec->multiout); -} - -static int conexant_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct conexant_spec *spec = codec->spec; - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} - -static int conexant_dig_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 conexant_spec *spec = codec->spec; - return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, - stream_tag, - format, substream); -} - -/* - * Analog capture - */ -static int conexant_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct conexant_spec *spec = codec->spec; - if (spec->capture_prepare) - spec->capture_prepare(codec); - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], - stream_tag, 0, format); - return 0; -} - -static int conexant_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct conexant_spec *spec = codec->spec; - snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); - if (spec->capture_cleanup) - spec->capture_cleanup(codec); - return 0; -} - - - -static const struct hda_pcm_stream conexant_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .nid = 0, /* fill later */ - .ops = { - .open = conexant_playback_pcm_open, - .prepare = conexant_playback_pcm_prepare, - .cleanup = conexant_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream conexant_pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .nid = 0, /* fill later */ - .ops = { - .prepare = conexant_capture_pcm_prepare, - .cleanup = conexant_capture_pcm_cleanup - }, -}; - - -static const struct hda_pcm_stream conexant_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .nid = 0, /* fill later */ - .ops = { - .open = conexant_dig_playback_pcm_open, - .close = conexant_dig_playback_pcm_close, - .prepare = conexant_dig_playback_pcm_prepare - }, -}; - -static const struct hda_pcm_stream conexant_pcm_digital_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in alc_build_pcms */ -}; - -static int cx5051_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct conexant_spec *spec = codec->spec; - spec->cur_adc = spec->adc_nids[spec->cur_adc_idx]; - spec->cur_adc_stream_tag = stream_tag; - spec->cur_adc_format = format; - snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format); - return 0; -} - -static int cx5051_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct conexant_spec *spec = codec->spec; - snd_hda_codec_cleanup_stream(codec, spec->cur_adc); - spec->cur_adc = 0; - return 0; -} - -static const struct hda_pcm_stream cx5051_pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .nid = 0, /* fill later */ - .ops = { - .prepare = cx5051_capture_pcm_prepare, - .cleanup = cx5051_capture_pcm_cleanup - }, -}; - -static int conexant_build_pcms(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; - - codec->num_pcms = 1; - codec->pcm_info = info; - - info->name = "CONEXANT Analog"; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = conexant_pcm_analog_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = - spec->multiout.max_channels; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = - spec->multiout.dac_nids[0]; - if (spec->capture_stream) - info->stream[SNDRV_PCM_STREAM_CAPTURE] = *spec->capture_stream; - else { - if (codec->vendor_id == 0x14f15051) - info->stream[SNDRV_PCM_STREAM_CAPTURE] = - cx5051_pcm_analog_capture; - else { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = - conexant_pcm_analog_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = - spec->num_adc_nids; - } - } - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; - - if (spec->multiout.dig_out_nid) { - info++; - codec->num_pcms++; - info->name = "Conexant Digital"; - info->pcm_type = HDA_PCM_TYPE_SPDIF; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = - conexant_pcm_digital_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = - spec->multiout.dig_out_nid; - if (spec->dig_in_nid) { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = - conexant_pcm_digital_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = - spec->dig_in_nid; - } - if (spec->slave_dig_outs[0]) - codec->slave_dig_outs = spec->slave_dig_outs; - } - - return 0; -} - -static int conexant_mux_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - - return snd_hda_input_mux_info(spec->input_mux, uinfo); -} - -static int conexant_mux_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - - ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; - return 0; -} - -static int conexant_mux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - - return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, - spec->capsrc_nids[adc_idx], - &spec->cur_mux[adc_idx]); -} - -static void conexant_set_power(struct hda_codec *codec, hda_nid_t fg, - unsigned int power_state) -{ - if (power_state == AC_PWRST_D3) - msleep(100); - snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, - power_state); - /* partial workaround for "azx_get_response timeout" */ - if (power_state == AC_PWRST_D0) - msleep(10); - snd_hda_codec_set_power_to_all(codec, fg, power_state, true); -} - -static int conexant_init(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->num_init_verbs; i++) - snd_hda_sequence_write(codec, spec->init_verbs[i]); - return 0; -} - -static void conexant_free(struct hda_codec *codec) -{ - snd_hda_detach_beep_device(codec); - kfree(codec->spec); -} - -static const struct snd_kcontrol_new cxt_capture_mixers[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = conexant_mux_enum_info, - .get = conexant_mux_enum_get, - .put = conexant_mux_enum_put - }, - {} -}; - -#ifdef CONFIG_SND_HDA_INPUT_BEEP -/* additional beep mixers; the actual parameters are overwritten at build */ -static const struct snd_kcontrol_new cxt_beep_mixer[] = { - HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT), - HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT), - { } /* end */ -}; -#endif - -static const char * const slave_pfxs[] = { - "Headphone", "Speaker", "Front", "Surround", "CLFE", - NULL -}; - -static int conexant_build_controls(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - unsigned int i; - int err; - - for (i = 0; i < spec->num_mixers; i++) { - err = snd_hda_add_new_ctls(codec, spec->mixers[i]); - if (err < 0) - return err; - } - if (spec->multiout.dig_out_nid) { - err = snd_hda_create_spdif_out_ctls(codec, - spec->multiout.dig_out_nid, - spec->multiout.dig_out_nid); - if (err < 0) - return err; - err = snd_hda_create_spdif_share_sw(codec, - &spec->multiout); - if (err < 0) - return err; - spec->multiout.share_spdif = 1; - } - if (spec->dig_in_nid) { - err = snd_hda_create_spdif_in_ctls(codec,spec->dig_in_nid); - if (err < 0) - return err; - } - - /* if we have no master control, let's create it */ - if (spec->vmaster_nid && - !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { - unsigned int vmaster_tlv[4]; - snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, - HDA_OUTPUT, vmaster_tlv); - err = snd_hda_add_vmaster(codec, "Master Playback Volume", - vmaster_tlv, slave_pfxs, - "Playback Volume"); - if (err < 0) - return err; - } - if (spec->vmaster_nid && - !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { - err = __snd_hda_add_vmaster(codec, "Master Playback Switch", - NULL, slave_pfxs, - "Playback Switch", true, - &spec->vmaster_mute.sw_kctl); - if (err < 0) - return err; - } - - if (spec->input_mux) { - err = snd_hda_add_new_ctls(codec, cxt_capture_mixers); - if (err < 0) - return err; - } - -#ifdef CONFIG_SND_HDA_INPUT_BEEP - /* create beep controls if needed */ - if (spec->beep_amp) { - const struct snd_kcontrol_new *knew; - for (knew = cxt_beep_mixer; knew->name; knew++) { - struct snd_kcontrol *kctl; - kctl = snd_ctl_new1(knew, codec); - if (!kctl) - return -ENOMEM; - kctl->private_value = spec->beep_amp; - err = snd_hda_ctl_add(codec, 0, kctl); - if (err < 0) - return err; - } - } -#endif - - return 0; -} - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static int conexant_suspend(struct hda_codec *codec, pm_message_t state) -{ - snd_hda_shutup_pins(codec); - return 0; -} -#endif - -static const struct hda_codec_ops conexant_patch_ops = { - .build_controls = conexant_build_controls, - .build_pcms = conexant_build_pcms, - .init = conexant_init, - .free = conexant_free, - .set_power_state = conexant_set_power, -#ifdef CONFIG_SND_HDA_POWER_SAVE - .suspend = conexant_suspend, -#endif - .reboot_notify = snd_hda_shutup_pins, -}; - -#ifdef CONFIG_SND_HDA_INPUT_BEEP -#define set_beep_amp(spec, nid, idx, dir) \ - ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) -#else -#define set_beep_amp(spec, nid, idx, dir) /* NOP */ -#endif - -static int patch_conexant_auto(struct hda_codec *codec); -/* - * EAPD control - * the private value = nid | (invert << 8) - */ - -#define cxt_eapd_info snd_ctl_boolean_mono_info - -static int cxt_eapd_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - int invert = (kcontrol->private_value >> 8) & 1; - if (invert) - ucontrol->value.integer.value[0] = !spec->cur_eapd; - else - ucontrol->value.integer.value[0] = spec->cur_eapd; - return 0; - -} - -static int cxt_eapd_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - int invert = (kcontrol->private_value >> 8) & 1; - hda_nid_t nid = kcontrol->private_value & 0xff; - unsigned int eapd; - - eapd = !!ucontrol->value.integer.value[0]; - if (invert) - eapd = !eapd; - if (eapd == spec->cur_eapd) - return 0; - - spec->cur_eapd = eapd; - snd_hda_codec_write_cache(codec, nid, - 0, AC_VERB_SET_EAPD_BTLENABLE, - eapd ? 0x02 : 0x00); - return 1; -} - -/* controls for test mode */ -#ifdef CONFIG_SND_DEBUG - -#define CXT_EAPD_SWITCH(xname, nid, mask) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ - .info = cxt_eapd_info, \ - .get = cxt_eapd_get, \ - .put = cxt_eapd_put, \ - .private_value = nid | (mask<<16) } - - - -static int conexant_ch_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode, - spec->num_channel_mode); -} - -static int conexant_ch_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode, - spec->num_channel_mode, - spec->multiout.max_channels); -} - -static int conexant_ch_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode, - spec->num_channel_mode, - &spec->multiout.max_channels); - if (err >= 0 && spec->need_dac_fix) - spec->multiout.num_dacs = spec->multiout.max_channels / 2; - return err; -} - -#define CXT_PIN_MODE(xname, nid, dir) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ - .info = conexant_ch_mode_info, \ - .get = conexant_ch_mode_get, \ - .put = conexant_ch_mode_put, \ - .private_value = nid | (dir<<16) } - -#endif /* CONFIG_SND_DEBUG */ - -/* Conexant 5045 specific */ - -static const hda_nid_t cxt5045_dac_nids[1] = { 0x19 }; -static const hda_nid_t cxt5045_adc_nids[1] = { 0x1a }; -static const hda_nid_t cxt5045_capsrc_nids[1] = { 0x1a }; -#define CXT5045_SPDIF_OUT 0x18 - -static const struct hda_channel_mode cxt5045_modes[1] = { - { 2, NULL }, -}; - -static const struct hda_input_mux cxt5045_capture_source = { - .num_items = 2, - .items = { - { "Internal Mic", 0x1 }, - { "Mic", 0x2 }, - } -}; - -static const struct hda_input_mux cxt5045_capture_source_benq = { - .num_items = 4, - .items = { - { "Internal Mic", 0x1 }, - { "Mic", 0x2 }, - { "Line", 0x3 }, - { "Mixer", 0x0 }, - } -}; - -static const struct hda_input_mux cxt5045_capture_source_hp530 = { - .num_items = 2, - .items = { - { "Mic", 0x1 }, - { "Internal Mic", 0x2 }, - } -}; - -/* turn on/off EAPD (+ mute HP) as a master switch */ -static int cxt5045_hp_master_sw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - unsigned int bits; - - if (!cxt_eapd_put(kcontrol, ucontrol)) - return 0; - - /* toggle internal speakers mute depending of presence of - * the headphone jack - */ - bits = (!spec->hp_present && spec->cur_eapd) ? 0 : HDA_AMP_MUTE; - snd_hda_codec_amp_stereo(codec, 0x10, HDA_OUTPUT, 0, - HDA_AMP_MUTE, bits); - - bits = spec->cur_eapd ? 0 : HDA_AMP_MUTE; - snd_hda_codec_amp_stereo(codec, 0x11, HDA_OUTPUT, 0, - HDA_AMP_MUTE, bits); - return 1; -} - -/* bind volumes of both NID 0x10 and 0x11 */ -static const struct hda_bind_ctls cxt5045_hp_bind_master_vol = { - .ops = &snd_hda_bind_vol, - .values = { - HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x11, 3, 0, HDA_OUTPUT), - 0 - }, -}; - -/* toggle input of built-in and mic jack appropriately */ -static void cxt5045_hp_automic(struct hda_codec *codec) -{ - static const struct hda_verb mic_jack_on[] = { - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - {} - }; - static const struct hda_verb mic_jack_off[] = { - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - {} - }; - unsigned int present; - - present = snd_hda_jack_detect(codec, 0x12); - if (present) - snd_hda_sequence_write(codec, mic_jack_on); - else - snd_hda_sequence_write(codec, mic_jack_off); -} - - -/* mute internal speaker if HP is plugged */ -static void cxt5045_hp_automute(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - unsigned int bits; - - spec->hp_present = snd_hda_jack_detect(codec, 0x11); - - bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0; - snd_hda_codec_amp_stereo(codec, 0x10, HDA_OUTPUT, 0, - HDA_AMP_MUTE, bits); -} - -/* unsolicited event for HP jack sensing */ -static void cxt5045_hp_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - res >>= 26; - switch (res) { - case CONEXANT_HP_EVENT: - cxt5045_hp_automute(codec); - break; - case CONEXANT_MIC_EVENT: - cxt5045_hp_automic(codec); - break; - - } -} - -static const struct snd_kcontrol_new cxt5045_mixers[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x1a, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x1a, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("PCM Playback Volume", 0x17, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x17, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x17, 0x2, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x17, 0x2, HDA_INPUT), - HDA_BIND_VOL("Master Playback Volume", &cxt5045_hp_bind_master_vol), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .info = cxt_eapd_info, - .get = cxt_eapd_get, - .put = cxt5045_hp_master_sw_put, - .private_value = 0x10, - }, - - {} -}; - -static const struct snd_kcontrol_new cxt5045_benq_mixers[] = { - HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x3, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x3, HDA_INPUT), - - {} -}; - -static const struct snd_kcontrol_new cxt5045_mixers_hp530[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x1a, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x1a, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("PCM Playback Volume", 0x17, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x17, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x2, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0x2, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x17, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x17, 0x1, HDA_INPUT), - HDA_BIND_VOL("Master Playback Volume", &cxt5045_hp_bind_master_vol), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .info = cxt_eapd_info, - .get = cxt_eapd_get, - .put = cxt5045_hp_master_sw_put, - .private_value = 0x10, - }, - - {} -}; - -static const struct hda_verb cxt5045_init_verbs[] = { - /* Line in, Mic */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 }, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 }, - /* HP, Amp */ - {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x10, AC_VERB_SET_CONNECT_SEL, 0x1}, - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x11, AC_VERB_SET_CONNECT_SEL, 0x1}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* Record selector: Internal mic */ - {0x1a, AC_VERB_SET_CONNECT_SEL,0x1}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, - AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17}, - /* SPDIF route: PCM */ - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - { 0x13, AC_VERB_SET_CONNECT_SEL, 0x0 }, - /* EAPD */ - {0x10, AC_VERB_SET_EAPD_BTLENABLE, 0x2 }, /* default on */ - { } /* end */ -}; - -static const struct hda_verb cxt5045_benq_init_verbs[] = { - /* Internal Mic, Mic */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 }, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 }, - /* Line In,HP, Amp */ - {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x10, AC_VERB_SET_CONNECT_SEL, 0x1}, - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x11, AC_VERB_SET_CONNECT_SEL, 0x1}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* Record selector: Internal mic */ - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x1}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, - AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17}, - /* SPDIF route: PCM */ - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x13, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* EAPD */ - {0x10, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ - { } /* end */ -}; - -static const struct hda_verb cxt5045_hp_sense_init_verbs[] = { - /* pin sensing on HP jack */ - {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT}, - { } /* end */ -}; - -static const struct hda_verb cxt5045_mic_sense_init_verbs[] = { - /* pin sensing on HP jack */ - {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT}, - { } /* end */ -}; - -#ifdef CONFIG_SND_DEBUG -/* Test configuration for debugging, modelled after the ALC260 test - * configuration. - */ -static const struct hda_input_mux cxt5045_test_capture_source = { - .num_items = 5, - .items = { - { "MIXER", 0x0 }, - { "MIC1 pin", 0x1 }, - { "LINE1 pin", 0x2 }, - { "HP-OUT pin", 0x3 }, - { "CD pin", 0x4 }, - }, -}; - -static const struct snd_kcontrol_new cxt5045_test_mixer[] = { - - /* Output controls */ - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x10, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x10, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("HP-OUT Playback Volume", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("HP-OUT Playback Switch", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("LINE1 Playback Volume", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("LINE1 Playback Switch", 0x12, 0x0, HDA_OUTPUT), - - /* Modes for retasking pin widgets */ - CXT_PIN_MODE("HP-OUT pin mode", 0x11, CXT_PIN_DIR_INOUT), - CXT_PIN_MODE("LINE1 pin mode", 0x12, CXT_PIN_DIR_INOUT), - - /* EAPD Switch Control */ - CXT_EAPD_SWITCH("External Amplifier", 0x10, 0x0), - - /* Loopback mixer controls */ - - HDA_CODEC_VOLUME("PCM Volume", 0x17, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("PCM Switch", 0x17, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("MIC1 pin Volume", 0x17, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("MIC1 pin Switch", 0x17, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("LINE1 pin Volume", 0x17, 0x2, HDA_INPUT), - HDA_CODEC_MUTE("LINE1 pin Switch", 0x17, 0x2, HDA_INPUT), - HDA_CODEC_VOLUME("HP-OUT pin Volume", 0x17, 0x3, HDA_INPUT), - HDA_CODEC_MUTE("HP-OUT pin Switch", 0x17, 0x3, HDA_INPUT), - HDA_CODEC_VOLUME("CD pin Volume", 0x17, 0x4, HDA_INPUT), - HDA_CODEC_MUTE("CD pin Switch", 0x17, 0x4, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Input Source", - .info = conexant_mux_enum_info, - .get = conexant_mux_enum_get, - .put = conexant_mux_enum_put, - }, - /* Audio input controls */ - HDA_CODEC_VOLUME("Capture Volume", 0x1a, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x1a, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct hda_verb cxt5045_test_init_verbs[] = { - /* Set connections */ - { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 }, - { 0x11, AC_VERB_SET_CONNECT_SEL, 0x0 }, - { 0x12, AC_VERB_SET_CONNECT_SEL, 0x0 }, - /* Enable retasking pins as output, initially without power amp */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - - /* Disable digital (SPDIF) pins initially, but users can enable - * them via a mixer switch. In the case of SPDIF-out, this initverb - * payload also sets the generation to 0, output to be in "consumer" - * PCM format, copyright asserted, no pre-emphasis and no validity - * control. - */ - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x18, AC_VERB_SET_DIGI_CONVERT_1, 0}, - - /* Unmute retasking pin widget output buffers since the default - * state appears to be output. As the pin mode is changed by the - * user the pin mode control will take care of enabling the pin's - * input/output buffers as needed. - */ - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* Mute capture amp left and right */ - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - - /* Set ADC connection select to match default mixer setting (mic1 - * pin) - */ - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x17, AC_VERB_SET_CONNECT_SEL, 0x01}, - - /* Mute all inputs to mixer widget (even unconnected ones) */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* Mixer */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* Mic1 pin */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* Line pin */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* HP pin */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */ - - { } -}; -#endif - - -/* initialize jack-sensing, too */ -static int cxt5045_init(struct hda_codec *codec) -{ - conexant_init(codec); - cxt5045_hp_automute(codec); - return 0; -} - - -enum { - CXT5045_LAPTOP_HPSENSE, - CXT5045_LAPTOP_MICSENSE, - CXT5045_LAPTOP_HPMICSENSE, - CXT5045_BENQ, - CXT5045_LAPTOP_HP530, -#ifdef CONFIG_SND_DEBUG - CXT5045_TEST, -#endif - CXT5045_AUTO, - CXT5045_MODELS -}; - -static const char * const cxt5045_models[CXT5045_MODELS] = { - [CXT5045_LAPTOP_HPSENSE] = "laptop-hpsense", - [CXT5045_LAPTOP_MICSENSE] = "laptop-micsense", - [CXT5045_LAPTOP_HPMICSENSE] = "laptop-hpmicsense", - [CXT5045_BENQ] = "benq", - [CXT5045_LAPTOP_HP530] = "laptop-hp530", -#ifdef CONFIG_SND_DEBUG - [CXT5045_TEST] = "test", -#endif - [CXT5045_AUTO] = "auto", -}; - -static const struct snd_pci_quirk cxt5045_cfg_tbl[] = { - SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HP530), - SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P105", CXT5045_LAPTOP_MICSENSE), - SND_PCI_QUIRK(0x152d, 0x0753, "Benq R55E", CXT5045_BENQ), - SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_LAPTOP_MICSENSE), - SND_PCI_QUIRK(0x1734, 0x10cb, "Fujitsu Si3515", CXT5045_LAPTOP_HPMICSENSE), - SND_PCI_QUIRK(0x1734, 0x110e, "Fujitsu V5505", - CXT5045_LAPTOP_HPMICSENSE), - SND_PCI_QUIRK(0x1509, 0x1e40, "FIC", CXT5045_LAPTOP_HPMICSENSE), - SND_PCI_QUIRK(0x1509, 0x2f05, "FIC", CXT5045_LAPTOP_HPMICSENSE), - SND_PCI_QUIRK(0x1509, 0x2f06, "FIC", CXT5045_LAPTOP_HPMICSENSE), - SND_PCI_QUIRK_MASK(0x1631, 0xff00, 0xc100, "Packard Bell", - CXT5045_LAPTOP_HPMICSENSE), - SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP_HPSENSE), - {} -}; - -static int patch_cxt5045(struct hda_codec *codec) -{ - struct conexant_spec *spec; - int board_config; - - board_config = snd_hda_check_board_config(codec, CXT5045_MODELS, - cxt5045_models, - cxt5045_cfg_tbl); - if (board_config < 0) - board_config = CXT5045_AUTO; /* model=auto as default */ - if (board_config == CXT5045_AUTO) - return patch_conexant_auto(codec); - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return -ENOMEM; - codec->spec = spec; - codec->single_adc_amp = 1; - - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = ARRAY_SIZE(cxt5045_dac_nids); - spec->multiout.dac_nids = cxt5045_dac_nids; - spec->multiout.dig_out_nid = CXT5045_SPDIF_OUT; - spec->num_adc_nids = 1; - spec->adc_nids = cxt5045_adc_nids; - spec->capsrc_nids = cxt5045_capsrc_nids; - spec->input_mux = &cxt5045_capture_source; - spec->num_mixers = 1; - spec->mixers[0] = cxt5045_mixers; - spec->num_init_verbs = 1; - spec->init_verbs[0] = cxt5045_init_verbs; - spec->spdif_route = 0; - spec->num_channel_mode = ARRAY_SIZE(cxt5045_modes); - spec->channel_mode = cxt5045_modes; - - set_beep_amp(spec, 0x16, 0, 1); - - codec->patch_ops = conexant_patch_ops; - - switch (board_config) { - case CXT5045_LAPTOP_HPSENSE: - codec->patch_ops.unsol_event = cxt5045_hp_unsol_event; - spec->input_mux = &cxt5045_capture_source; - spec->num_init_verbs = 2; - spec->init_verbs[1] = cxt5045_hp_sense_init_verbs; - spec->mixers[0] = cxt5045_mixers; - codec->patch_ops.init = cxt5045_init; - break; - case CXT5045_LAPTOP_MICSENSE: - codec->patch_ops.unsol_event = cxt5045_hp_unsol_event; - spec->input_mux = &cxt5045_capture_source; - spec->num_init_verbs = 2; - spec->init_verbs[1] = cxt5045_mic_sense_init_verbs; - spec->mixers[0] = cxt5045_mixers; - codec->patch_ops.init = cxt5045_init; - break; - default: - case CXT5045_LAPTOP_HPMICSENSE: - codec->patch_ops.unsol_event = cxt5045_hp_unsol_event; - spec->input_mux = &cxt5045_capture_source; - spec->num_init_verbs = 3; - spec->init_verbs[1] = cxt5045_hp_sense_init_verbs; - spec->init_verbs[2] = cxt5045_mic_sense_init_verbs; - spec->mixers[0] = cxt5045_mixers; - codec->patch_ops.init = cxt5045_init; - break; - case CXT5045_BENQ: - codec->patch_ops.unsol_event = cxt5045_hp_unsol_event; - spec->input_mux = &cxt5045_capture_source_benq; - spec->num_init_verbs = 1; - spec->init_verbs[0] = cxt5045_benq_init_verbs; - spec->mixers[0] = cxt5045_mixers; - spec->mixers[1] = cxt5045_benq_mixers; - spec->num_mixers = 2; - codec->patch_ops.init = cxt5045_init; - break; - case CXT5045_LAPTOP_HP530: - codec->patch_ops.unsol_event = cxt5045_hp_unsol_event; - spec->input_mux = &cxt5045_capture_source_hp530; - spec->num_init_verbs = 2; - spec->init_verbs[1] = cxt5045_hp_sense_init_verbs; - spec->mixers[0] = cxt5045_mixers_hp530; - codec->patch_ops.init = cxt5045_init; - break; -#ifdef CONFIG_SND_DEBUG - case CXT5045_TEST: - spec->input_mux = &cxt5045_test_capture_source; - spec->mixers[0] = cxt5045_test_mixer; - spec->init_verbs[0] = cxt5045_test_init_verbs; - break; - -#endif - } - - switch (codec->subsystem_id >> 16) { - case 0x103c: - case 0x1631: - case 0x1734: - case 0x17aa: - /* HP, Packard Bell, Fujitsu-Siemens & Lenovo laptops have - * really bad sound over 0dB on NID 0x17. Fix max PCM level to - * 0 dB (originally it has 0x2b steps with 0dB offset 0x14) - */ - snd_hda_override_amp_caps(codec, 0x17, HDA_INPUT, - (0x14 << AC_AMPCAP_OFFSET_SHIFT) | - (0x14 << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (1 << AC_AMPCAP_MUTE_SHIFT)); - break; - } - - if (spec->beep_amp) - snd_hda_attach_beep_device(codec, spec->beep_amp); - - return 0; -} - - -/* Conexant 5047 specific */ -#define CXT5047_SPDIF_OUT 0x11 - -static const hda_nid_t cxt5047_dac_nids[1] = { 0x10 }; /* 0x1c */ -static const hda_nid_t cxt5047_adc_nids[1] = { 0x12 }; -static const hda_nid_t cxt5047_capsrc_nids[1] = { 0x1a }; - -static const struct hda_channel_mode cxt5047_modes[1] = { - { 2, NULL }, -}; - -static const struct hda_input_mux cxt5047_toshiba_capture_source = { - .num_items = 2, - .items = { - { "ExtMic", 0x2 }, - { "Line-In", 0x1 }, - } -}; - -/* turn on/off EAPD (+ mute HP) as a master switch */ -static int cxt5047_hp_master_sw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - unsigned int bits; - - if (!cxt_eapd_put(kcontrol, ucontrol)) - return 0; - - /* toggle internal speakers mute depending of presence of - * the headphone jack - */ - bits = (!spec->hp_present && spec->cur_eapd) ? 0 : HDA_AMP_MUTE; - /* NOTE: Conexat codec needs the index for *OUTPUT* amp of - * pin widgets unlike other codecs. In this case, we need to - * set index 0x01 for the volume from the mixer amp 0x19. - */ - snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0x01, - HDA_AMP_MUTE, bits); - bits = spec->cur_eapd ? 0 : HDA_AMP_MUTE; - snd_hda_codec_amp_stereo(codec, 0x13, HDA_OUTPUT, 0, - HDA_AMP_MUTE, bits); - return 1; -} - -/* mute internal speaker if HP is plugged */ -static void cxt5047_hp_automute(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - unsigned int bits; - - spec->hp_present = snd_hda_jack_detect(codec, 0x13); - - bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0; - /* See the note in cxt5047_hp_master_sw_put */ - snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0x01, - HDA_AMP_MUTE, bits); -} - -/* toggle input of built-in and mic jack appropriately */ -static void cxt5047_hp_automic(struct hda_codec *codec) -{ - static const struct hda_verb mic_jack_on[] = { - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {} - }; - static const struct hda_verb mic_jack_off[] = { - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {} - }; - unsigned int present; - - present = snd_hda_jack_detect(codec, 0x15); - if (present) - snd_hda_sequence_write(codec, mic_jack_on); - else - snd_hda_sequence_write(codec, mic_jack_off); -} - -/* unsolicited event for HP jack sensing */ -static void cxt5047_hp_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - switch (res >> 26) { - case CONEXANT_HP_EVENT: - cxt5047_hp_automute(codec); - break; - case CONEXANT_MIC_EVENT: - cxt5047_hp_automic(codec); - break; - } -} - -static const struct snd_kcontrol_new cxt5047_base_mixers[] = { - HDA_CODEC_VOLUME("Mic Playback Volume", 0x19, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x19, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x1a, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x03, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x12, 0x03, HDA_INPUT), - HDA_CODEC_VOLUME("PCM Volume", 0x10, 0x00, HDA_OUTPUT), - HDA_CODEC_MUTE("PCM Switch", 0x10, 0x00, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .info = cxt_eapd_info, - .get = cxt_eapd_get, - .put = cxt5047_hp_master_sw_put, - .private_value = 0x13, - }, - - {} -}; - -static const struct snd_kcontrol_new cxt5047_hp_spk_mixers[] = { - /* See the note in cxt5047_hp_master_sw_put */ - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x1d, 0x01, HDA_OUTPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x13, 0x00, HDA_OUTPUT), - {} -}; - -static const struct snd_kcontrol_new cxt5047_hp_only_mixers[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x13, 0x00, HDA_OUTPUT), - { } /* end */ -}; - -static const struct hda_verb cxt5047_init_verbs[] = { - /* Line in, Mic, Built-in Mic */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_50 }, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_50 }, - /* HP, Speaker */ - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, - {0x13, AC_VERB_SET_CONNECT_SEL, 0x0}, /* mixer(0x19) */ - {0x1d, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mixer(0x19) */ - /* Record selector: Mic */ - {0x12, AC_VERB_SET_CONNECT_SEL,0x03}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, - AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17}, - {0x1A, AC_VERB_SET_CONNECT_SEL,0x02}, - {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, - AC_AMP_SET_OUTPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x00}, - {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, - AC_AMP_SET_OUTPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x03}, - /* SPDIF route: PCM */ - { 0x18, AC_VERB_SET_CONNECT_SEL, 0x0 }, - /* Enable unsolicited events */ - {0x13, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT}, - { } /* end */ -}; - -/* configuration for Toshiba Laptops */ -static const struct hda_verb cxt5047_toshiba_init_verbs[] = { - {0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x0}, /* default off */ - {} -}; - -/* Test configuration for debugging, modelled after the ALC260 test - * configuration. - */ -#ifdef CONFIG_SND_DEBUG -static const struct hda_input_mux cxt5047_test_capture_source = { - .num_items = 4, - .items = { - { "LINE1 pin", 0x0 }, - { "MIC1 pin", 0x1 }, - { "MIC2 pin", 0x2 }, - { "CD pin", 0x3 }, - }, -}; - -static const struct snd_kcontrol_new cxt5047_test_mixer[] = { - - /* Output only controls */ - HDA_CODEC_VOLUME("OutAmp-1 Volume", 0x10, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("OutAmp-1 Switch", 0x10,0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("OutAmp-2 Volume", 0x1c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("OutAmp-2 Switch", 0x1c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x1d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x1d, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("HeadPhone Playback Volume", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("HeadPhone Playback Switch", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Line1-Out Playback Volume", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Line1-Out Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Line2-Out Playback Volume", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Line2-Out Playback Switch", 0x15, 0x0, HDA_OUTPUT), - - /* Modes for retasking pin widgets */ - CXT_PIN_MODE("LINE1 pin mode", 0x14, CXT_PIN_DIR_INOUT), - CXT_PIN_MODE("MIC1 pin mode", 0x15, CXT_PIN_DIR_INOUT), - - /* EAPD Switch Control */ - CXT_EAPD_SWITCH("External Amplifier", 0x13, 0x0), - - /* Loopback mixer controls */ - HDA_CODEC_VOLUME("MIC1 Playback Volume", 0x12, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("MIC1 Playback Switch", 0x12, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("MIC2 Playback Volume", 0x12, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("MIC2 Playback Switch", 0x12, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("LINE Playback Volume", 0x12, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("LINE Playback Switch", 0x12, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x12, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x12, 0x04, HDA_INPUT), - - HDA_CODEC_VOLUME("Capture-1 Volume", 0x19, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture-1 Switch", 0x19, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Capture-2 Volume", 0x19, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Capture-2 Switch", 0x19, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Capture-3 Volume", 0x19, 0x2, HDA_INPUT), - HDA_CODEC_MUTE("Capture-3 Switch", 0x19, 0x2, HDA_INPUT), - HDA_CODEC_VOLUME("Capture-4 Volume", 0x19, 0x3, HDA_INPUT), - HDA_CODEC_MUTE("Capture-4 Switch", 0x19, 0x3, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Input Source", - .info = conexant_mux_enum_info, - .get = conexant_mux_enum_get, - .put = conexant_mux_enum_put, - }, - HDA_CODEC_VOLUME("Mic Boost Volume", 0x1a, 0x0, HDA_OUTPUT), - - { } /* end */ -}; - -static const struct hda_verb cxt5047_test_init_verbs[] = { - /* Enable retasking pins as output, initially without power amp */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - - /* Disable digital (SPDIF) pins initially, but users can enable - * them via a mixer switch. In the case of SPDIF-out, this initverb - * payload also sets the generation to 0, output to be in "consumer" - * PCM format, copyright asserted, no pre-emphasis and no validity - * control. - */ - {0x18, AC_VERB_SET_DIGI_CONVERT_1, 0}, - - /* Ensure mic1, mic2, line1 pin widgets take input from the - * OUT1 sum bus when acting as an output. - */ - {0x1a, AC_VERB_SET_CONNECT_SEL, 0}, - {0x1b, AC_VERB_SET_CONNECT_SEL, 0}, - - /* Start with output sum widgets muted and their output gains at min */ - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - - /* Unmute retasking pin widget output buffers since the default - * state appears to be output. As the pin mode is changed by the - * user the pin mode control will take care of enabling the pin's - * input/output buffers as needed. - */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* Mute capture amp left and right */ - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - - /* Set ADC connection select to match default mixer setting (mic1 - * pin) - */ - {0x12, AC_VERB_SET_CONNECT_SEL, 0x00}, - - /* Mute all inputs to mixer widget (even unconnected ones) */ - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */ - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */ - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */ - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */ - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */ - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */ - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */ - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */ - - { } -}; -#endif - - -/* initialize jack-sensing, too */ -static int cxt5047_hp_init(struct hda_codec *codec) -{ - conexant_init(codec); - cxt5047_hp_automute(codec); - return 0; -} - - -enum { - CXT5047_LAPTOP, /* Laptops w/o EAPD support */ - CXT5047_LAPTOP_HP, /* Some HP laptops */ - CXT5047_LAPTOP_EAPD, /* Laptops with EAPD support */ -#ifdef CONFIG_SND_DEBUG - CXT5047_TEST, -#endif - CXT5047_AUTO, - CXT5047_MODELS -}; - -static const char * const cxt5047_models[CXT5047_MODELS] = { - [CXT5047_LAPTOP] = "laptop", - [CXT5047_LAPTOP_HP] = "laptop-hp", - [CXT5047_LAPTOP_EAPD] = "laptop-eapd", -#ifdef CONFIG_SND_DEBUG - [CXT5047_TEST] = "test", -#endif - [CXT5047_AUTO] = "auto", -}; - -static const struct snd_pci_quirk cxt5047_cfg_tbl[] = { - SND_PCI_QUIRK(0x103c, 0x30a5, "HP DV5200T/DV8000T", CXT5047_LAPTOP_HP), - SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP DV Series", - CXT5047_LAPTOP), - SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P100", CXT5047_LAPTOP_EAPD), - {} -}; - -static int patch_cxt5047(struct hda_codec *codec) -{ - struct conexant_spec *spec; - int board_config; - - board_config = snd_hda_check_board_config(codec, CXT5047_MODELS, - cxt5047_models, - cxt5047_cfg_tbl); - if (board_config < 0) - board_config = CXT5047_AUTO; /* model=auto as default */ - if (board_config == CXT5047_AUTO) - return patch_conexant_auto(codec); - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return -ENOMEM; - codec->spec = spec; - codec->pin_amp_workaround = 1; - - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = ARRAY_SIZE(cxt5047_dac_nids); - spec->multiout.dac_nids = cxt5047_dac_nids; - spec->multiout.dig_out_nid = CXT5047_SPDIF_OUT; - spec->num_adc_nids = 1; - spec->adc_nids = cxt5047_adc_nids; - spec->capsrc_nids = cxt5047_capsrc_nids; - spec->num_mixers = 1; - spec->mixers[0] = cxt5047_base_mixers; - spec->num_init_verbs = 1; - spec->init_verbs[0] = cxt5047_init_verbs; - spec->spdif_route = 0; - spec->num_channel_mode = ARRAY_SIZE(cxt5047_modes), - spec->channel_mode = cxt5047_modes, - - codec->patch_ops = conexant_patch_ops; - - switch (board_config) { - case CXT5047_LAPTOP: - spec->num_mixers = 2; - spec->mixers[1] = cxt5047_hp_spk_mixers; - codec->patch_ops.unsol_event = cxt5047_hp_unsol_event; - break; - case CXT5047_LAPTOP_HP: - spec->num_mixers = 2; - spec->mixers[1] = cxt5047_hp_only_mixers; - codec->patch_ops.unsol_event = cxt5047_hp_unsol_event; - codec->patch_ops.init = cxt5047_hp_init; - break; - case CXT5047_LAPTOP_EAPD: - spec->input_mux = &cxt5047_toshiba_capture_source; - spec->num_mixers = 2; - spec->mixers[1] = cxt5047_hp_spk_mixers; - spec->num_init_verbs = 2; - spec->init_verbs[1] = cxt5047_toshiba_init_verbs; - codec->patch_ops.unsol_event = cxt5047_hp_unsol_event; - break; -#ifdef CONFIG_SND_DEBUG - case CXT5047_TEST: - spec->input_mux = &cxt5047_test_capture_source; - spec->mixers[0] = cxt5047_test_mixer; - spec->init_verbs[0] = cxt5047_test_init_verbs; - codec->patch_ops.unsol_event = cxt5047_hp_unsol_event; -#endif - } - spec->vmaster_nid = 0x13; - - switch (codec->subsystem_id >> 16) { - case 0x103c: - /* HP laptops have really bad sound over 0 dB on NID 0x10. - * Fix max PCM level to 0 dB (originally it has 0x1e steps - * with 0 dB offset 0x17) - */ - snd_hda_override_amp_caps(codec, 0x10, HDA_INPUT, - (0x17 << AC_AMPCAP_OFFSET_SHIFT) | - (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (1 << AC_AMPCAP_MUTE_SHIFT)); - break; - } - - return 0; -} - -/* Conexant 5051 specific */ -static const hda_nid_t cxt5051_dac_nids[1] = { 0x10 }; -static const hda_nid_t cxt5051_adc_nids[2] = { 0x14, 0x15 }; - -static const struct hda_channel_mode cxt5051_modes[1] = { - { 2, NULL }, -}; - -static void cxt5051_update_speaker(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - unsigned int pinctl; - /* headphone pin */ - pinctl = (spec->hp_present && spec->cur_eapd) ? PIN_HP : 0; - snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pinctl); - /* speaker pin */ - pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0; - snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pinctl); - /* on ideapad there is an additional speaker (subwoofer) to mute */ - if (spec->ideapad) - snd_hda_codec_write(codec, 0x1b, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - pinctl); -} - -/* turn on/off EAPD (+ mute HP) as a master switch */ -static int cxt5051_hp_master_sw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - - if (!cxt_eapd_put(kcontrol, ucontrol)) - return 0; - cxt5051_update_speaker(codec); - return 1; -} - -/* toggle input of built-in and mic jack appropriately */ -static void cxt5051_portb_automic(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - unsigned int present; - - if (!(spec->auto_mic & AUTO_MIC_PORTB)) - return; - present = snd_hda_jack_detect(codec, 0x17); - snd_hda_codec_write(codec, 0x14, 0, - AC_VERB_SET_CONNECT_SEL, - present ? 0x01 : 0x00); -} - -/* switch the current ADC according to the jack state */ -static void cxt5051_portc_automic(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - unsigned int present; - hda_nid_t new_adc; - - if (!(spec->auto_mic & AUTO_MIC_PORTC)) - return; - present = snd_hda_jack_detect(codec, 0x18); - if (present) - spec->cur_adc_idx = 1; - else - spec->cur_adc_idx = 0; - new_adc = spec->adc_nids[spec->cur_adc_idx]; - if (spec->cur_adc && spec->cur_adc != new_adc) { - /* stream is running, let's swap the current ADC */ - __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); - spec->cur_adc = new_adc; - snd_hda_codec_setup_stream(codec, new_adc, - spec->cur_adc_stream_tag, 0, - spec->cur_adc_format); - } -} - -/* mute internal speaker if HP is plugged */ -static void cxt5051_hp_automute(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - - spec->hp_present = snd_hda_jack_detect(codec, 0x16); - cxt5051_update_speaker(codec); -} - -/* unsolicited event for HP jack sensing */ -static void cxt5051_hp_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - switch (res >> 26) { - case CONEXANT_HP_EVENT: - cxt5051_hp_automute(codec); - break; - case CXT5051_PORTB_EVENT: - cxt5051_portb_automic(codec); - break; - case CXT5051_PORTC_EVENT: - cxt5051_portc_automic(codec); - break; - } -} - -static const struct snd_kcontrol_new cxt5051_playback_mixers[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .info = cxt_eapd_info, - .get = cxt_eapd_get, - .put = cxt5051_hp_master_sw_put, - .private_value = 0x1a, - }, - {} -}; - -static const struct snd_kcontrol_new cxt5051_capture_mixers[] = { - HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Volume", 0x14, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Mic Switch", 0x14, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Docking Mic Volume", 0x15, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Docking Mic Switch", 0x15, 0x00, HDA_INPUT), - {} -}; - -static const struct snd_kcontrol_new cxt5051_hp_mixers[] = { - HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Volume", 0x15, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Mic Switch", 0x15, 0x00, HDA_INPUT), - {} -}; - -static const struct snd_kcontrol_new cxt5051_hp_dv6736_mixers[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x14, 0x00, HDA_INPUT), - {} -}; - -static const struct snd_kcontrol_new cxt5051_f700_mixers[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x14, 0x01, HDA_INPUT), - {} -}; - -static const struct snd_kcontrol_new cxt5051_toshiba_mixers[] = { - HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Volume", 0x14, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Mic Switch", 0x14, 0x01, HDA_INPUT), - {} -}; - -static const struct hda_verb cxt5051_init_verbs[] = { - /* Line in, Mic */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, - /* SPK */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* HP, Amp */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* DAC1 */ - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Record selector: Internal mic */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44}, - /* SPDIF route: PCM */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* EAPD */ - {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ - {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT}, - { } /* end */ -}; - -static const struct hda_verb cxt5051_hp_dv6736_init_verbs[] = { - /* Line in, Mic */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0}, - {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0}, - /* SPK */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* HP, Amp */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* DAC1 */ - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Record selector: Internal mic */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44}, - {0x14, AC_VERB_SET_CONNECT_SEL, 0x1}, - /* SPDIF route: PCM */ - {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* EAPD */ - {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ - {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT}, - { } /* end */ -}; - -static const struct hda_verb cxt5051_f700_init_verbs[] = { - /* Line in, Mic */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0}, - {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0}, - /* SPK */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* HP, Amp */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* DAC1 */ - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Record selector: Internal mic */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44}, - {0x14, AC_VERB_SET_CONNECT_SEL, 0x1}, - /* SPDIF route: PCM */ - {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* EAPD */ - {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ - {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT}, - { } /* end */ -}; - -static void cxt5051_init_mic_port(struct hda_codec *codec, hda_nid_t nid, - unsigned int event) -{ - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | event); -} - -static const struct hda_verb cxt5051_ideapad_init_verbs[] = { - /* Subwoofer */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, - { } /* end */ -}; - -/* initialize jack-sensing, too */ -static int cxt5051_init(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - - conexant_init(codec); - - if (spec->auto_mic & AUTO_MIC_PORTB) - cxt5051_init_mic_port(codec, 0x17, CXT5051_PORTB_EVENT); - if (spec->auto_mic & AUTO_MIC_PORTC) - cxt5051_init_mic_port(codec, 0x18, CXT5051_PORTC_EVENT); - - if (codec->patch_ops.unsol_event) { - cxt5051_hp_automute(codec); - cxt5051_portb_automic(codec); - cxt5051_portc_automic(codec); - } - return 0; -} - - -enum { - CXT5051_LAPTOP, /* Laptops w/ EAPD support */ - CXT5051_HP, /* no docking */ - CXT5051_HP_DV6736, /* HP without mic switch */ - CXT5051_F700, /* HP Compaq Presario F700 */ - CXT5051_TOSHIBA, /* Toshiba M300 & co */ - CXT5051_IDEAPAD, /* Lenovo IdeaPad Y430 */ - CXT5051_AUTO, /* auto-parser */ - CXT5051_MODELS -}; - -static const char *const cxt5051_models[CXT5051_MODELS] = { - [CXT5051_LAPTOP] = "laptop", - [CXT5051_HP] = "hp", - [CXT5051_HP_DV6736] = "hp-dv6736", - [CXT5051_F700] = "hp-700", - [CXT5051_TOSHIBA] = "toshiba", - [CXT5051_IDEAPAD] = "ideapad", - [CXT5051_AUTO] = "auto", -}; - -static const struct snd_pci_quirk cxt5051_cfg_tbl[] = { - SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV6736", CXT5051_HP_DV6736), - SND_PCI_QUIRK(0x103c, 0x360b, "Compaq Presario CQ60", CXT5051_HP), - SND_PCI_QUIRK(0x103c, 0x30ea, "Compaq Presario F700", CXT5051_F700), - SND_PCI_QUIRK(0x1179, 0xff50, "Toshiba M30x", CXT5051_TOSHIBA), - SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board", - CXT5051_LAPTOP), - SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP), - SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo IdeaPad", CXT5051_IDEAPAD), - {} -}; - -static int patch_cxt5051(struct hda_codec *codec) -{ - struct conexant_spec *spec; - int board_config; - - board_config = snd_hda_check_board_config(codec, CXT5051_MODELS, - cxt5051_models, - cxt5051_cfg_tbl); - if (board_config < 0) - board_config = CXT5051_AUTO; /* model=auto as default */ - if (board_config == CXT5051_AUTO) - return patch_conexant_auto(codec); - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return -ENOMEM; - codec->spec = spec; - codec->pin_amp_workaround = 1; - - codec->patch_ops = conexant_patch_ops; - codec->patch_ops.init = cxt5051_init; - - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = ARRAY_SIZE(cxt5051_dac_nids); - spec->multiout.dac_nids = cxt5051_dac_nids; - spec->multiout.dig_out_nid = CXT5051_SPDIF_OUT; - spec->num_adc_nids = 1; /* not 2; via auto-mic switch */ - spec->adc_nids = cxt5051_adc_nids; - spec->num_mixers = 2; - spec->mixers[0] = cxt5051_capture_mixers; - spec->mixers[1] = cxt5051_playback_mixers; - spec->num_init_verbs = 1; - spec->init_verbs[0] = cxt5051_init_verbs; - spec->spdif_route = 0; - spec->num_channel_mode = ARRAY_SIZE(cxt5051_modes); - spec->channel_mode = cxt5051_modes; - spec->cur_adc = 0; - spec->cur_adc_idx = 0; - - set_beep_amp(spec, 0x13, 0, HDA_OUTPUT); - - codec->patch_ops.unsol_event = cxt5051_hp_unsol_event; - - spec->auto_mic = AUTO_MIC_PORTB | AUTO_MIC_PORTC; - switch (board_config) { - case CXT5051_HP: - spec->mixers[0] = cxt5051_hp_mixers; - break; - case CXT5051_HP_DV6736: - spec->init_verbs[0] = cxt5051_hp_dv6736_init_verbs; - spec->mixers[0] = cxt5051_hp_dv6736_mixers; - spec->auto_mic = 0; - break; - case CXT5051_F700: - spec->init_verbs[0] = cxt5051_f700_init_verbs; - spec->mixers[0] = cxt5051_f700_mixers; - spec->auto_mic = 0; - break; - case CXT5051_TOSHIBA: - spec->mixers[0] = cxt5051_toshiba_mixers; - spec->auto_mic = AUTO_MIC_PORTB; - break; - case CXT5051_IDEAPAD: - spec->init_verbs[spec->num_init_verbs++] = - cxt5051_ideapad_init_verbs; - spec->ideapad = 1; - break; - } - - if (spec->beep_amp) - snd_hda_attach_beep_device(codec, spec->beep_amp); - - return 0; -} - -/* Conexant 5066 specific */ - -static const hda_nid_t cxt5066_dac_nids[1] = { 0x10 }; -static const hda_nid_t cxt5066_adc_nids[3] = { 0x14, 0x15, 0x16 }; -static const hda_nid_t cxt5066_capsrc_nids[1] = { 0x17 }; -static const hda_nid_t cxt5066_digout_pin_nids[2] = { 0x20, 0x22 }; - -/* OLPC's microphone port is DC coupled for use with external sensors, - * therefore we use a 50% mic bias in order to center the input signal with - * the DC input range of the codec. */ -#define CXT5066_OLPC_EXT_MIC_BIAS PIN_VREF50 - -static const struct hda_channel_mode cxt5066_modes[1] = { - { 2, NULL }, -}; - -#define HP_PRESENT_PORT_A (1 << 0) -#define HP_PRESENT_PORT_D (1 << 1) -#define hp_port_a_present(spec) ((spec)->hp_present & HP_PRESENT_PORT_A) -#define hp_port_d_present(spec) ((spec)->hp_present & HP_PRESENT_PORT_D) - -static void cxt5066_update_speaker(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - unsigned int pinctl; - - snd_printdd("CXT5066: update speaker, hp_present=%d, cur_eapd=%d\n", - spec->hp_present, spec->cur_eapd); - - /* Port A (HP) */ - pinctl = (hp_port_a_present(spec) && spec->cur_eapd) ? PIN_HP : 0; - snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pinctl); - - /* Port D (HP/LO) */ - pinctl = spec->cur_eapd ? spec->port_d_mode : 0; - if (spec->dell_automute || spec->thinkpad) { - /* Mute if Port A is connected */ - if (hp_port_a_present(spec)) - pinctl = 0; - } else { - /* Thinkpad/Dell doesn't give pin-D status */ - if (!hp_port_d_present(spec)) - pinctl = 0; - } - snd_hda_codec_write(codec, 0x1c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pinctl); - - /* CLASS_D AMP */ - pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0; - snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pinctl); -} - -/* turn on/off EAPD (+ mute HP) as a master switch */ -static int cxt5066_hp_master_sw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - - if (!cxt_eapd_put(kcontrol, ucontrol)) - return 0; - - cxt5066_update_speaker(codec); - return 1; -} - -static const struct hda_input_mux cxt5066_olpc_dc_bias = { - .num_items = 3, - .items = { - { "Off", PIN_IN }, - { "50%", PIN_VREF50 }, - { "80%", PIN_VREF80 }, - }, -}; - -static int cxt5066_set_olpc_dc_bias(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - /* Even though port F is the DC input, the bias is controlled on port B. - * we also leave that port as an active input (but unselected) in DC mode - * just in case that is necessary to make the bias setting take effect. */ - return snd_hda_codec_write_cache(codec, 0x1a, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - cxt5066_olpc_dc_bias.items[spec->dc_input_bias].index); -} - -/* OLPC defers mic widget control until when capture is started because the - * microphone LED comes on as soon as these settings are put in place. if we - * did this before recording, it would give the false indication that recording - * is happening when it is not. */ -static void cxt5066_olpc_select_mic(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - if (!spec->recording) - return; - - if (spec->dc_enable) { - /* in DC mode we ignore presence detection and just use the jack - * through our special DC port */ - const struct hda_verb enable_dc_mode[] = { - /* disble internal mic, port C */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - - /* enable DC capture, port F */ - {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {}, - }; - - snd_hda_sequence_write(codec, enable_dc_mode); - /* port B input disabled (and bias set) through the following call */ - cxt5066_set_olpc_dc_bias(codec); - return; - } - - /* disable DC (port F) */ - snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0); - - /* external mic, port B */ - snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - spec->ext_mic_present ? CXT5066_OLPC_EXT_MIC_BIAS : 0); - - /* internal mic, port C */ - snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - spec->ext_mic_present ? 0 : PIN_VREF80); -} - -/* toggle input of built-in and mic jack appropriately */ -static void cxt5066_olpc_automic(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - unsigned int present; - - if (spec->dc_enable) /* don't do presence detection in DC mode */ - return; - - present = snd_hda_codec_read(codec, 0x1a, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - if (present) - snd_printdd("CXT5066: external microphone detected\n"); - else - snd_printdd("CXT5066: external microphone absent\n"); - - snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_CONNECT_SEL, - present ? 0 : 1); - spec->ext_mic_present = !!present; - - cxt5066_olpc_select_mic(codec); -} - -/* toggle input of built-in digital mic and mic jack appropriately */ -static void cxt5066_vostro_automic(struct hda_codec *codec) -{ - unsigned int present; - - struct hda_verb ext_mic_present[] = { - /* enable external mic, port B */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - - /* switch to external mic input */ - {0x17, AC_VERB_SET_CONNECT_SEL, 0}, - {0x14, AC_VERB_SET_CONNECT_SEL, 0}, - - /* disable internal digital mic */ - {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {} - }; - static const struct hda_verb ext_mic_absent[] = { - /* enable internal mic, port C */ - {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - - /* switch to internal mic input */ - {0x14, AC_VERB_SET_CONNECT_SEL, 2}, - - /* disable external mic, port B */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {} - }; - - present = snd_hda_jack_detect(codec, 0x1a); - if (present) { - snd_printdd("CXT5066: external microphone detected\n"); - snd_hda_sequence_write(codec, ext_mic_present); - } else { - snd_printdd("CXT5066: external microphone absent\n"); - snd_hda_sequence_write(codec, ext_mic_absent); - } -} - -/* toggle input of built-in digital mic and mic jack appropriately */ -static void cxt5066_ideapad_automic(struct hda_codec *codec) -{ - unsigned int present; - - struct hda_verb ext_mic_present[] = { - {0x14, AC_VERB_SET_CONNECT_SEL, 0}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {} - }; - static const struct hda_verb ext_mic_absent[] = { - {0x14, AC_VERB_SET_CONNECT_SEL, 2}, - {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {} - }; - - present = snd_hda_jack_detect(codec, 0x1b); - if (present) { - snd_printdd("CXT5066: external microphone detected\n"); - snd_hda_sequence_write(codec, ext_mic_present); - } else { - snd_printdd("CXT5066: external microphone absent\n"); - snd_hda_sequence_write(codec, ext_mic_absent); - } -} - - -/* toggle input of built-in digital mic and mic jack appropriately */ -static void cxt5066_asus_automic(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_jack_detect(codec, 0x1b); - snd_printdd("CXT5066: external microphone present=%d\n", present); - snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_CONNECT_SEL, - present ? 1 : 0); -} - - -/* toggle input of built-in digital mic and mic jack appropriately */ -static void cxt5066_hp_laptop_automic(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_jack_detect(codec, 0x1b); - snd_printdd("CXT5066: external microphone present=%d\n", present); - snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_CONNECT_SEL, - present ? 1 : 3); -} - - -/* toggle input of built-in digital mic and mic jack appropriately - order is: external mic -> dock mic -> interal mic */ -static void cxt5066_thinkpad_automic(struct hda_codec *codec) -{ - unsigned int ext_present, dock_present; - - static const struct hda_verb ext_mic_present[] = { - {0x14, AC_VERB_SET_CONNECT_SEL, 0}, - {0x17, AC_VERB_SET_CONNECT_SEL, 1}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {} - }; - static const struct hda_verb dock_mic_present[] = { - {0x14, AC_VERB_SET_CONNECT_SEL, 0}, - {0x17, AC_VERB_SET_CONNECT_SEL, 0}, - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {} - }; - static const struct hda_verb ext_mic_absent[] = { - {0x14, AC_VERB_SET_CONNECT_SEL, 2}, - {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {} - }; - - ext_present = snd_hda_jack_detect(codec, 0x1b); - dock_present = snd_hda_jack_detect(codec, 0x1a); - if (ext_present) { - snd_printdd("CXT5066: external microphone detected\n"); - snd_hda_sequence_write(codec, ext_mic_present); - } else if (dock_present) { - snd_printdd("CXT5066: dock microphone detected\n"); - snd_hda_sequence_write(codec, dock_mic_present); - } else { - snd_printdd("CXT5066: external microphone absent\n"); - snd_hda_sequence_write(codec, ext_mic_absent); - } -} - -/* mute internal speaker if HP is plugged */ -static void cxt5066_hp_automute(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - unsigned int portA, portD; - - /* Port A */ - portA = snd_hda_jack_detect(codec, 0x19); - - /* Port D */ - portD = snd_hda_jack_detect(codec, 0x1c); - - spec->hp_present = portA ? HP_PRESENT_PORT_A : 0; - spec->hp_present |= portD ? HP_PRESENT_PORT_D : 0; - snd_printdd("CXT5066: hp automute portA=%x portD=%x present=%d\n", - portA, portD, spec->hp_present); - cxt5066_update_speaker(codec); -} - -/* Dispatch the right mic autoswitch function */ -static void cxt5066_automic(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - - if (spec->dell_vostro) - cxt5066_vostro_automic(codec); - else if (spec->ideapad) - cxt5066_ideapad_automic(codec); - else if (spec->thinkpad) - cxt5066_thinkpad_automic(codec); - else if (spec->hp_laptop) - cxt5066_hp_laptop_automic(codec); - else if (spec->asus) - cxt5066_asus_automic(codec); -} - -/* unsolicited event for jack sensing */ -static void cxt5066_olpc_unsol_event(struct hda_codec *codec, unsigned int res) -{ - struct conexant_spec *spec = codec->spec; - snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26); - switch (res >> 26) { - case CONEXANT_HP_EVENT: - cxt5066_hp_automute(codec); - break; - case CONEXANT_MIC_EVENT: - /* ignore mic events in DC mode; we're always using the jack */ - if (!spec->dc_enable) - cxt5066_olpc_automic(codec); - break; - } -} - -/* unsolicited event for jack sensing */ -static void cxt5066_unsol_event(struct hda_codec *codec, unsigned int res) -{ - snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26); - switch (res >> 26) { - case CONEXANT_HP_EVENT: - cxt5066_hp_automute(codec); - break; - case CONEXANT_MIC_EVENT: - cxt5066_automic(codec); - break; - } -} - - -static const struct hda_input_mux cxt5066_analog_mic_boost = { - .num_items = 5, - .items = { - { "0dB", 0 }, - { "10dB", 1 }, - { "20dB", 2 }, - { "30dB", 3 }, - { "40dB", 4 }, - }, -}; - -static void cxt5066_set_mic_boost(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - snd_hda_codec_write_cache(codec, 0x17, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT | - cxt5066_analog_mic_boost.items[spec->mic_boost].index); - if (spec->ideapad || spec->thinkpad) { - /* adjust the internal mic as well...it is not through 0x17 */ - snd_hda_codec_write_cache(codec, 0x23, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_INPUT | - cxt5066_analog_mic_boost. - items[spec->mic_boost].index); - } -} - -static int cxt5066_mic_boost_mux_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - return snd_hda_input_mux_info(&cxt5066_analog_mic_boost, uinfo); -} - -static int cxt5066_mic_boost_mux_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - ucontrol->value.enumerated.item[0] = spec->mic_boost; - return 0; -} - -static int cxt5066_mic_boost_mux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - const struct hda_input_mux *imux = &cxt5066_analog_mic_boost; - unsigned int idx; - idx = ucontrol->value.enumerated.item[0]; - if (idx >= imux->num_items) - idx = imux->num_items - 1; - - spec->mic_boost = idx; - if (!spec->dc_enable) - cxt5066_set_mic_boost(codec); - return 1; -} - -static void cxt5066_enable_dc(struct hda_codec *codec) -{ - const struct hda_verb enable_dc_mode[] = { - /* disable gain */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* switch to DC input */ - {0x17, AC_VERB_SET_CONNECT_SEL, 3}, - {} - }; - - /* configure as input source */ - snd_hda_sequence_write(codec, enable_dc_mode); - cxt5066_olpc_select_mic(codec); /* also sets configured bias */ -} - -static void cxt5066_disable_dc(struct hda_codec *codec) -{ - /* reconfigure input source */ - cxt5066_set_mic_boost(codec); - /* automic also selects the right mic if we're recording */ - cxt5066_olpc_automic(codec); -} - -static int cxt5066_olpc_dc_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - ucontrol->value.integer.value[0] = spec->dc_enable; - return 0; -} - -static int cxt5066_olpc_dc_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - int dc_enable = !!ucontrol->value.integer.value[0]; - - if (dc_enable == spec->dc_enable) - return 0; - - spec->dc_enable = dc_enable; - if (dc_enable) - cxt5066_enable_dc(codec); - else - cxt5066_disable_dc(codec); - - return 1; -} - -static int cxt5066_olpc_dc_bias_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - return snd_hda_input_mux_info(&cxt5066_olpc_dc_bias, uinfo); -} - -static int cxt5066_olpc_dc_bias_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - ucontrol->value.enumerated.item[0] = spec->dc_input_bias; - return 0; -} - -static int cxt5066_olpc_dc_bias_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - const struct hda_input_mux *imux = &cxt5066_analog_mic_boost; - unsigned int idx; - - idx = ucontrol->value.enumerated.item[0]; - if (idx >= imux->num_items) - idx = imux->num_items - 1; - - spec->dc_input_bias = idx; - if (spec->dc_enable) - cxt5066_set_olpc_dc_bias(codec); - return 1; -} - -static void cxt5066_olpc_capture_prepare(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - /* mark as recording and configure the microphone widget so that the - * recording LED comes on. */ - spec->recording = 1; - cxt5066_olpc_select_mic(codec); -} - -static void cxt5066_olpc_capture_cleanup(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - const struct hda_verb disable_mics[] = { - /* disable external mic, port B */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - - /* disble internal mic, port C */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - - /* disable DC capture, port F */ - {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {}, - }; - - snd_hda_sequence_write(codec, disable_mics); - spec->recording = 0; -} - -static void conexant_check_dig_outs(struct hda_codec *codec, - const hda_nid_t *dig_pins, - int num_pins) -{ - struct conexant_spec *spec = codec->spec; - hda_nid_t *nid_loc = &spec->multiout.dig_out_nid; - int i; - - for (i = 0; i < num_pins; i++, dig_pins++) { - unsigned int cfg = snd_hda_codec_get_pincfg(codec, *dig_pins); - if (get_defcfg_connect(cfg) == AC_JACK_PORT_NONE) - continue; - if (snd_hda_get_connections(codec, *dig_pins, nid_loc, 1) != 1) - continue; - if (spec->slave_dig_outs[0]) - nid_loc++; - else - nid_loc = spec->slave_dig_outs; - } -} - -static const struct hda_input_mux cxt5066_capture_source = { - .num_items = 4, - .items = { - { "Mic B", 0 }, - { "Mic C", 1 }, - { "Mic E", 2 }, - { "Mic F", 3 }, - }, -}; - -static const struct hda_bind_ctls cxt5066_bind_capture_vol_others = { - .ops = &snd_hda_bind_vol, - .values = { - HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_INPUT), - HDA_COMPOSE_AMP_VAL(0x14, 3, 2, HDA_INPUT), - 0 - }, -}; - -static const struct hda_bind_ctls cxt5066_bind_capture_sw_others = { - .ops = &snd_hda_bind_sw, - .values = { - HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_INPUT), - HDA_COMPOSE_AMP_VAL(0x14, 3, 2, HDA_INPUT), - 0 - }, -}; - -static const struct snd_kcontrol_new cxt5066_mixer_master[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT), - {} -}; - -static const struct snd_kcontrol_new cxt5066_mixer_master_olpc[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Volume", - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | - SNDRV_CTL_ELEM_ACCESS_TLV_READ | - SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, - .subdevice = HDA_SUBDEV_AMP_FLAG, - .info = snd_hda_mixer_amp_volume_info, - .get = snd_hda_mixer_amp_volume_get, - .put = snd_hda_mixer_amp_volume_put, - .tlv = { .c = snd_hda_mixer_amp_tlv }, - /* offset by 28 volume steps to limit minimum gain to -46dB */ - .private_value = - HDA_COMPOSE_AMP_VAL_OFS(0x10, 3, 0, HDA_OUTPUT, 28), - }, - {} -}; - -static const struct snd_kcontrol_new cxt5066_mixer_olpc_dc[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "DC Mode Enable Switch", - .info = snd_ctl_boolean_mono_info, - .get = cxt5066_olpc_dc_get, - .put = cxt5066_olpc_dc_put, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "DC Input Bias Enum", - .info = cxt5066_olpc_dc_bias_enum_info, - .get = cxt5066_olpc_dc_bias_enum_get, - .put = cxt5066_olpc_dc_bias_enum_put, - }, - {} -}; - -static const struct snd_kcontrol_new cxt5066_mixers[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .info = cxt_eapd_info, - .get = cxt_eapd_get, - .put = cxt5066_hp_master_sw_put, - .private_value = 0x1d, - }, - - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Analog Mic Boost Capture Enum", - .info = cxt5066_mic_boost_mux_enum_info, - .get = cxt5066_mic_boost_mux_enum_get, - .put = cxt5066_mic_boost_mux_enum_put, - }, - - HDA_BIND_VOL("Capture Volume", &cxt5066_bind_capture_vol_others), - HDA_BIND_SW("Capture Switch", &cxt5066_bind_capture_sw_others), - {} -}; - -static const struct snd_kcontrol_new cxt5066_vostro_mixers[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Internal Mic Boost Capture Enum", - .info = cxt5066_mic_boost_mux_enum_info, - .get = cxt5066_mic_boost_mux_enum_get, - .put = cxt5066_mic_boost_mux_enum_put, - .private_value = 0x23 | 0x100, - }, - {} -}; - -static const struct hda_verb cxt5066_init_verbs[] = { - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port B */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port C */ - {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port F */ - {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port E */ - - /* Speakers */ - {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ - - /* HP, Amp */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ - - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1c, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ - - /* DAC1 */ - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* Node 14 connections: 0x17 0x18 0x23 0x24 0x27 */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2) | 0x50}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - - /* no digital microphone support yet */ - {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - - /* Audio input selector */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3}, - - /* SPDIF route: PCM */ - {0x20, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x22, AC_VERB_SET_CONNECT_SEL, 0x0}, - - {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - - /* EAPD */ - {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ - - /* not handling these yet */ - {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, 0}, - {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, 0}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, 0}, - {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, 0}, - {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 0}, - {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, 0}, - {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, 0}, - {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, 0}, - { } /* end */ -}; - -static const struct hda_verb cxt5066_init_verbs_olpc[] = { - /* Port A: headphones */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ - - /* Port B: external microphone */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - - /* Port C: internal microphone */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - - /* Port D: unused */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - - /* Port E: unused, but has primary EAPD */ - {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ - - /* Port F: external DC input through microphone port */ - {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - - /* Port G: internal speakers */ - {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ - - /* DAC1 */ - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* DAC2: unused */ - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - - /* Disable digital microphone port */ - {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - - /* Audio input selectors */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - - /* Disable SPDIF */ - {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - - /* enable unsolicited events for Port A and B */ - {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT}, - {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT}, - { } /* end */ -}; - -static const struct hda_verb cxt5066_init_verbs_vostro[] = { - /* Port A: headphones */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ - - /* Port B: external microphone */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - - /* Port C: unused */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - - /* Port D: unused */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - - /* Port E: unused, but has primary EAPD */ - {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ - - /* Port F: unused */ - {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - - /* Port G: internal speakers */ - {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ - - /* DAC1 */ - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* DAC2: unused */ - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - - /* Digital microphone port */ - {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - - /* Audio input selectors */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - - /* Disable SPDIF */ - {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - - /* enable unsolicited events for Port A and B */ - {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT}, - {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT}, - { } /* end */ -}; - -static const struct hda_verb cxt5066_init_verbs_ideapad[] = { - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port B */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port C */ - {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port F */ - {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port E */ - - /* Speakers */ - {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ - - /* HP, Amp */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ - - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1c, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ - - /* DAC1 */ - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* Node 14 connections: 0x17 0x18 0x23 0x24 0x27 */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2) | 0x50}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - {0x14, AC_VERB_SET_CONNECT_SEL, 2}, /* default to internal mic */ - - /* Audio input selector */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x2}, - {0x17, AC_VERB_SET_CONNECT_SEL, 1}, /* route ext mic */ - - /* SPDIF route: PCM */ - {0x20, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x22, AC_VERB_SET_CONNECT_SEL, 0x0}, - - {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - - /* internal microphone */ - {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* enable internal mic */ - - /* EAPD */ - {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ - - {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT}, - { } /* end */ -}; - -static const struct hda_verb cxt5066_init_verbs_thinkpad[] = { - {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port F */ - {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port E */ - - /* Port G: internal speakers */ - {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ - - /* Port A: HP, Amp */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ - - /* Port B: Mic Dock */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - - /* Port C: Mic */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - - /* Port D: HP Dock, Amp */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x1c, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ - - /* DAC1 */ - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* Node 14 connections: 0x17 0x18 0x23 0x24 0x27 */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2) | 0x50}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - {0x14, AC_VERB_SET_CONNECT_SEL, 2}, /* default to internal mic */ - - /* Audio input selector */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x2}, - {0x17, AC_VERB_SET_CONNECT_SEL, 1}, /* route ext mic */ - - /* SPDIF route: PCM */ - {0x20, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x22, AC_VERB_SET_CONNECT_SEL, 0x0}, - - {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - - /* internal microphone */ - {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* enable internal mic */ - - /* EAPD */ - {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ - - /* enable unsolicited events for Port A, B, C and D */ - {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT}, - {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT}, - {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT}, - { } /* end */ -}; - -static const struct hda_verb cxt5066_init_verbs_portd_lo[] = { - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - { } /* end */ -}; - - -static const struct hda_verb cxt5066_init_verbs_hp_laptop[] = { - {0x14, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT}, - { } /* end */ -}; - -/* initialize jack-sensing, too */ -static int cxt5066_init(struct hda_codec *codec) -{ - snd_printdd("CXT5066: init\n"); - conexant_init(codec); - if (codec->patch_ops.unsol_event) { - cxt5066_hp_automute(codec); - cxt5066_automic(codec); - } - cxt5066_set_mic_boost(codec); - return 0; -} - -static int cxt5066_olpc_init(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - snd_printdd("CXT5066: init\n"); - conexant_init(codec); - cxt5066_hp_automute(codec); - if (!spec->dc_enable) { - cxt5066_set_mic_boost(codec); - cxt5066_olpc_automic(codec); - } else { - cxt5066_enable_dc(codec); - } - return 0; -} - -enum { - CXT5066_LAPTOP, /* Laptops w/ EAPD support */ - CXT5066_DELL_LAPTOP, /* Dell Laptop */ - CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */ - CXT5066_DELL_VOSTRO, /* Dell Vostro 1015i */ - CXT5066_IDEAPAD, /* Lenovo IdeaPad U150 */ - CXT5066_THINKPAD, /* Lenovo ThinkPad T410s, others? */ - CXT5066_ASUS, /* Asus K52JU, Lenovo G560 - Int mic at 0x1a and Ext mic at 0x1b */ - CXT5066_HP_LAPTOP, /* HP Laptop */ - CXT5066_AUTO, /* BIOS auto-parser */ - CXT5066_MODELS -}; - -static const char * const cxt5066_models[CXT5066_MODELS] = { - [CXT5066_LAPTOP] = "laptop", - [CXT5066_DELL_LAPTOP] = "dell-laptop", - [CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5", - [CXT5066_DELL_VOSTRO] = "dell-vostro", - [CXT5066_IDEAPAD] = "ideapad", - [CXT5066_THINKPAD] = "thinkpad", - [CXT5066_ASUS] = "asus", - [CXT5066_HP_LAPTOP] = "hp-laptop", - [CXT5066_AUTO] = "auto", -}; - -static const struct snd_pci_quirk cxt5066_cfg_tbl[] = { - SND_PCI_QUIRK(0x1025, 0x054c, "Acer Aspire 3830TG", CXT5066_AUTO), - SND_PCI_QUIRK_MASK(0x1025, 0xff00, 0x0400, "Acer", CXT5066_IDEAPAD), - SND_PCI_QUIRK(0x1028, 0x02d8, "Dell Vostro", CXT5066_DELL_VOSTRO), - SND_PCI_QUIRK(0x1028, 0x02f5, "Dell Vostro 320", CXT5066_IDEAPAD), - SND_PCI_QUIRK(0x1028, 0x0401, "Dell Vostro 1014", CXT5066_DELL_VOSTRO), - SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTRO), - SND_PCI_QUIRK(0x1028, 0x0408, "Dell Inspiron One 19T", CXT5066_IDEAPAD), - SND_PCI_QUIRK(0x1028, 0x050f, "Dell Inspiron", CXT5066_IDEAPAD), - SND_PCI_QUIRK(0x1028, 0x0510, "Dell Vostro", CXT5066_IDEAPAD), - SND_PCI_QUIRK(0x103c, 0x360b, "HP G60", CXT5066_HP_LAPTOP), - SND_PCI_QUIRK(0x1043, 0x13f3, "Asus A52J", CXT5066_ASUS), - SND_PCI_QUIRK(0x1043, 0x1643, "Asus K52JU", CXT5066_ASUS), - SND_PCI_QUIRK(0x1043, 0x1993, "Asus U50F", CXT5066_ASUS), - SND_PCI_QUIRK(0x1179, 0xff1e, "Toshiba Satellite C650D", CXT5066_IDEAPAD), - SND_PCI_QUIRK(0x1179, 0xff50, "Toshiba Satellite P500-PSPGSC-01800T", CXT5066_OLPC_XO_1_5), - SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board", - CXT5066_LAPTOP), - SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5), - SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400s", CXT5066_THINKPAD), - SND_PCI_QUIRK(0x17aa, 0x21c5, "Thinkpad Edge 13", CXT5066_THINKPAD), - SND_PCI_QUIRK(0x17aa, 0x21c6, "Thinkpad Edge 13", CXT5066_ASUS), - SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo T510", CXT5066_AUTO), - SND_PCI_QUIRK(0x17aa, 0x21cf, "Lenovo T520 & W520", CXT5066_AUTO), - SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT5066_THINKPAD), - SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT5066_THINKPAD), - SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo U350", CXT5066_ASUS), - SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo G560", CXT5066_ASUS), - SND_PCI_QUIRK(0x17aa, 0x3938, "Lenovo G565", CXT5066_AUTO), - SND_PCI_QUIRK(0x1b0a, 0x2092, "CyberpowerPC Gamer Xplorer N57001", CXT5066_AUTO), - {} -}; - -static int patch_cxt5066(struct hda_codec *codec) -{ - struct conexant_spec *spec; - int board_config; - - board_config = snd_hda_check_board_config(codec, CXT5066_MODELS, - cxt5066_models, cxt5066_cfg_tbl); - if (board_config < 0) - board_config = CXT5066_AUTO; /* model=auto as default */ - if (board_config == CXT5066_AUTO) - return patch_conexant_auto(codec); - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return -ENOMEM; - codec->spec = spec; - - codec->patch_ops = conexant_patch_ops; - codec->patch_ops.init = conexant_init; - - spec->dell_automute = 0; - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = ARRAY_SIZE(cxt5066_dac_nids); - spec->multiout.dac_nids = cxt5066_dac_nids; - conexant_check_dig_outs(codec, cxt5066_digout_pin_nids, - ARRAY_SIZE(cxt5066_digout_pin_nids)); - spec->num_adc_nids = 1; - spec->adc_nids = cxt5066_adc_nids; - spec->capsrc_nids = cxt5066_capsrc_nids; - spec->input_mux = &cxt5066_capture_source; - - spec->port_d_mode = PIN_HP; - - spec->num_init_verbs = 1; - spec->init_verbs[0] = cxt5066_init_verbs; - spec->num_channel_mode = ARRAY_SIZE(cxt5066_modes); - spec->channel_mode = cxt5066_modes; - spec->cur_adc = 0; - spec->cur_adc_idx = 0; - - set_beep_amp(spec, 0x13, 0, HDA_OUTPUT); - - switch (board_config) { - default: - case CXT5066_LAPTOP: - spec->mixers[spec->num_mixers++] = cxt5066_mixer_master; - spec->mixers[spec->num_mixers++] = cxt5066_mixers; - break; - case CXT5066_DELL_LAPTOP: - spec->mixers[spec->num_mixers++] = cxt5066_mixer_master; - spec->mixers[spec->num_mixers++] = cxt5066_mixers; - - spec->port_d_mode = PIN_OUT; - spec->init_verbs[spec->num_init_verbs] = cxt5066_init_verbs_portd_lo; - spec->num_init_verbs++; - spec->dell_automute = 1; - break; - case CXT5066_ASUS: - case CXT5066_HP_LAPTOP: - codec->patch_ops.init = cxt5066_init; - codec->patch_ops.unsol_event = cxt5066_unsol_event; - spec->init_verbs[spec->num_init_verbs] = - cxt5066_init_verbs_hp_laptop; - spec->num_init_verbs++; - spec->hp_laptop = board_config == CXT5066_HP_LAPTOP; - spec->asus = board_config == CXT5066_ASUS; - spec->mixers[spec->num_mixers++] = cxt5066_mixer_master; - spec->mixers[spec->num_mixers++] = cxt5066_mixers; - /* no S/PDIF out */ - if (board_config == CXT5066_HP_LAPTOP) - spec->multiout.dig_out_nid = 0; - /* input source automatically selected */ - spec->input_mux = NULL; - spec->port_d_mode = 0; - spec->mic_boost = 3; /* default 30dB gain */ - break; - - case CXT5066_OLPC_XO_1_5: - codec->patch_ops.init = cxt5066_olpc_init; - codec->patch_ops.unsol_event = cxt5066_olpc_unsol_event; - spec->init_verbs[0] = cxt5066_init_verbs_olpc; - spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc; - spec->mixers[spec->num_mixers++] = cxt5066_mixer_olpc_dc; - spec->mixers[spec->num_mixers++] = cxt5066_mixers; - spec->port_d_mode = 0; - spec->mic_boost = 3; /* default 30dB gain */ - - /* no S/PDIF out */ - spec->multiout.dig_out_nid = 0; - - /* input source automatically selected */ - spec->input_mux = NULL; - - /* our capture hooks which allow us to turn on the microphone LED - * at the right time */ - spec->capture_prepare = cxt5066_olpc_capture_prepare; - spec->capture_cleanup = cxt5066_olpc_capture_cleanup; - break; - case CXT5066_DELL_VOSTRO: - codec->patch_ops.init = cxt5066_init; - codec->patch_ops.unsol_event = cxt5066_unsol_event; - spec->init_verbs[0] = cxt5066_init_verbs_vostro; - spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc; - spec->mixers[spec->num_mixers++] = cxt5066_mixers; - spec->mixers[spec->num_mixers++] = cxt5066_vostro_mixers; - spec->port_d_mode = 0; - spec->dell_vostro = 1; - spec->mic_boost = 3; /* default 30dB gain */ - - /* no S/PDIF out */ - spec->multiout.dig_out_nid = 0; - - /* input source automatically selected */ - spec->input_mux = NULL; - break; - case CXT5066_IDEAPAD: - codec->patch_ops.init = cxt5066_init; - codec->patch_ops.unsol_event = cxt5066_unsol_event; - spec->mixers[spec->num_mixers++] = cxt5066_mixer_master; - spec->mixers[spec->num_mixers++] = cxt5066_mixers; - spec->init_verbs[0] = cxt5066_init_verbs_ideapad; - spec->port_d_mode = 0; - spec->ideapad = 1; - spec->mic_boost = 2; /* default 20dB gain */ - - /* no S/PDIF out */ - spec->multiout.dig_out_nid = 0; - - /* input source automatically selected */ - spec->input_mux = NULL; - break; - case CXT5066_THINKPAD: - codec->patch_ops.init = cxt5066_init; - codec->patch_ops.unsol_event = cxt5066_unsol_event; - spec->mixers[spec->num_mixers++] = cxt5066_mixer_master; - spec->mixers[spec->num_mixers++] = cxt5066_mixers; - spec->init_verbs[0] = cxt5066_init_verbs_thinkpad; - spec->thinkpad = 1; - spec->port_d_mode = PIN_OUT; - spec->mic_boost = 2; /* default 20dB gain */ - - /* no S/PDIF out */ - spec->multiout.dig_out_nid = 0; - - /* input source automatically selected */ - spec->input_mux = NULL; - break; - } - - if (spec->beep_amp) - snd_hda_attach_beep_device(codec, spec->beep_amp); - - return 0; -} - -/* - * Automatic parser for CX20641 & co - */ - -static int cx_auto_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct conexant_spec *spec = codec->spec; - hda_nid_t adc = spec->imux_info[spec->cur_mux[0]].adc; - if (spec->adc_switching) { - spec->cur_adc = adc; - spec->cur_adc_stream_tag = stream_tag; - spec->cur_adc_format = format; - } - snd_hda_codec_setup_stream(codec, adc, stream_tag, 0, format); - return 0; -} - -static int cx_auto_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct conexant_spec *spec = codec->spec; - snd_hda_codec_cleanup_stream(codec, spec->cur_adc); - spec->cur_adc = 0; - return 0; -} - -static const struct hda_pcm_stream cx_auto_pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .nid = 0, /* fill later */ - .ops = { - .prepare = cx_auto_capture_pcm_prepare, - .cleanup = cx_auto_capture_pcm_cleanup - }, -}; - -static const hda_nid_t cx_auto_adc_nids[] = { 0x14 }; - -#define get_connection_index(codec, mux, nid)\ - snd_hda_get_conn_index(codec, mux, nid, 0) - -/* get an unassigned DAC from the given list. - * Return the nid if found and reduce the DAC list, or return zero if - * not found - */ -static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t *dacs, int *num_dacs) -{ - int i, nums = *num_dacs; - hda_nid_t ret = 0; - - for (i = 0; i < nums; i++) { - if (get_connection_index(codec, pin, dacs[i]) >= 0) { - ret = dacs[i]; - break; - } - } - if (!ret) - return 0; - if (--nums > 0) - memmove(dacs, dacs + 1, nums * sizeof(hda_nid_t)); - *num_dacs = nums; - return ret; -} - -#define MAX_AUTO_DACS 5 - -#define DAC_SLAVE_FLAG 0x8000 /* filled dac is a slave */ - -/* fill analog DAC list from the widget tree */ -static int fill_cx_auto_dacs(struct hda_codec *codec, hda_nid_t *dacs) -{ - hda_nid_t nid, end_nid; - int nums = 0; - - end_nid = codec->start_nid + codec->num_nodes; - for (nid = codec->start_nid; nid < end_nid; nid++) { - unsigned int wcaps = get_wcaps(codec, nid); - unsigned int type = get_wcaps_type(wcaps); - if (type == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL)) { - dacs[nums++] = nid; - if (nums >= MAX_AUTO_DACS) - break; - } - } - return nums; -} - -/* fill pin_dac_pair list from the pin and dac list */ -static int fill_dacs_for_pins(struct hda_codec *codec, hda_nid_t *pins, - int num_pins, hda_nid_t *dacs, int *rest, - struct pin_dac_pair *filled, int nums, - int type) -{ - int i, start = nums; - - for (i = 0; i < num_pins; i++, nums++) { - filled[nums].pin = pins[i]; - filled[nums].type = type; - filled[nums].dac = get_unassigned_dac(codec, pins[i], dacs, rest); - if (filled[nums].dac) - continue; - if (filled[start].dac && get_connection_index(codec, pins[i], filled[start].dac) >= 0) { - filled[nums].dac = filled[start].dac | DAC_SLAVE_FLAG; - continue; - } - if (filled[0].dac && get_connection_index(codec, pins[i], filled[0].dac) >= 0) { - filled[nums].dac = filled[0].dac | DAC_SLAVE_FLAG; - continue; - } - snd_printdd("Failed to find a DAC for pin 0x%x", pins[i]); - } - return nums; -} - -/* parse analog output paths */ -static void cx_auto_parse_output(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t dacs[MAX_AUTO_DACS]; - int i, j, nums, rest; - - rest = fill_cx_auto_dacs(codec, dacs); - /* parse all analog output pins */ - nums = fill_dacs_for_pins(codec, cfg->line_out_pins, cfg->line_outs, - dacs, &rest, spec->dac_info, 0, - AUTO_PIN_LINE_OUT); - nums = fill_dacs_for_pins(codec, cfg->hp_pins, cfg->hp_outs, - dacs, &rest, spec->dac_info, nums, - AUTO_PIN_HP_OUT); - nums = fill_dacs_for_pins(codec, cfg->speaker_pins, cfg->speaker_outs, - dacs, &rest, spec->dac_info, nums, - AUTO_PIN_SPEAKER_OUT); - spec->dac_info_filled = nums; - /* fill multiout struct */ - for (i = 0; i < nums; i++) { - hda_nid_t dac = spec->dac_info[i].dac; - if (!dac || (dac & DAC_SLAVE_FLAG)) - continue; - switch (spec->dac_info[i].type) { - case AUTO_PIN_LINE_OUT: - spec->private_dac_nids[spec->multiout.num_dacs] = dac; - spec->multiout.num_dacs++; - break; - case AUTO_PIN_HP_OUT: - case AUTO_PIN_SPEAKER_OUT: - if (!spec->multiout.hp_nid) { - spec->multiout.hp_nid = dac; - break; - } - for (j = 0; j < ARRAY_SIZE(spec->multiout.extra_out_nid); j++) - if (!spec->multiout.extra_out_nid[j]) { - spec->multiout.extra_out_nid[j] = dac; - break; - } - break; - } - } - spec->multiout.dac_nids = spec->private_dac_nids; - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - for (i = 0; i < cfg->hp_outs; i++) { - if (is_jack_detectable(codec, cfg->hp_pins[i])) { - spec->auto_mute = 1; - break; - } - } - if (spec->auto_mute && - cfg->line_out_pins[0] && - cfg->line_out_type != AUTO_PIN_SPEAKER_OUT && - cfg->line_out_pins[0] != cfg->hp_pins[0] && - cfg->line_out_pins[0] != cfg->speaker_pins[0]) { - for (i = 0; i < cfg->line_outs; i++) { - if (is_jack_detectable(codec, cfg->line_out_pins[i])) { - spec->detect_line = 1; - break; - } - } - spec->automute_lines = spec->detect_line; - } - - spec->vmaster_nid = spec->private_dac_nids[0]; -} - -static void cx_auto_turn_eapd(struct hda_codec *codec, int num_pins, - hda_nid_t *pins, bool on); - -static void do_automute(struct hda_codec *codec, int num_pins, - hda_nid_t *pins, bool on) -{ - struct conexant_spec *spec = codec->spec; - int i; - for (i = 0; i < num_pins; i++) - snd_hda_codec_write(codec, pins[i], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - on ? PIN_OUT : 0); - if (spec->pin_eapd_ctrls) - cx_auto_turn_eapd(codec, num_pins, pins, on); -} - -static int detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) -{ - int i, present = 0; - - for (i = 0; i < num_pins; i++) { - hda_nid_t nid = pins[i]; - if (!nid || !is_jack_detectable(codec, nid)) - break; - present |= snd_hda_jack_detect(codec, nid); - } - return present; -} - -/* auto-mute/unmute speaker and line outs according to headphone jack */ -static void cx_auto_update_speakers(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int on = 1; - - /* turn on HP EAPD when HP jacks are present */ - if (spec->pin_eapd_ctrls) { - if (spec->auto_mute) - on = spec->hp_present; - cx_auto_turn_eapd(codec, cfg->hp_outs, cfg->hp_pins, on); - } - - /* mute speakers in auto-mode if HP or LO jacks are plugged */ - if (spec->auto_mute) - on = !(spec->hp_present || - (spec->detect_line && spec->line_present)); - do_automute(codec, cfg->speaker_outs, cfg->speaker_pins, on); - - /* toggle line-out mutes if needed, too */ - /* if LO is a copy of either HP or Speaker, don't need to handle it */ - if (cfg->line_out_pins[0] == cfg->hp_pins[0] || - cfg->line_out_pins[0] == cfg->speaker_pins[0]) - return; - if (spec->auto_mute) { - /* mute LO in auto-mode when HP jack is present */ - if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT || - spec->automute_lines) - on = !spec->hp_present; - else - on = 1; - } - do_automute(codec, cfg->line_outs, cfg->line_out_pins, on); -} - -static void cx_auto_hp_automute(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - - if (!spec->auto_mute) - return; - spec->hp_present = detect_jacks(codec, cfg->hp_outs, cfg->hp_pins); - cx_auto_update_speakers(codec); -} - -static void cx_auto_line_automute(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - - if (!spec->auto_mute || !spec->detect_line) - return; - spec->line_present = detect_jacks(codec, cfg->line_outs, - cfg->line_out_pins); - cx_auto_update_speakers(codec); -} - -static int cx_automute_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - static const char * const texts2[] = { - "Disabled", "Enabled" - }; - static const char * const texts3[] = { - "Disabled", "Speaker Only", "Line Out+Speaker" - }; - const char * const *texts; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - if (spec->automute_hp_lo) { - uinfo->value.enumerated.items = 3; - texts = texts3; - } else { - uinfo->value.enumerated.items = 2; - texts = texts2; - } - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; -} - -static int cx_automute_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - unsigned int val; - if (!spec->auto_mute) - val = 0; - else if (!spec->automute_lines) - val = 1; - else - val = 2; - ucontrol->value.enumerated.item[0] = val; - return 0; -} - -static int cx_automute_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - - switch (ucontrol->value.enumerated.item[0]) { - case 0: - if (!spec->auto_mute) - return 0; - spec->auto_mute = 0; - break; - case 1: - if (spec->auto_mute && !spec->automute_lines) - return 0; - spec->auto_mute = 1; - spec->automute_lines = 0; - break; - case 2: - if (!spec->automute_hp_lo) - return -EINVAL; - if (spec->auto_mute && spec->automute_lines) - return 0; - spec->auto_mute = 1; - spec->automute_lines = 1; - break; - default: - return -EINVAL; - } - cx_auto_update_speakers(codec); - return 1; -} - -static const struct snd_kcontrol_new cx_automute_mode_enum[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Auto-Mute Mode", - .info = cx_automute_mode_info, - .get = cx_automute_mode_get, - .put = cx_automute_mode_put, - }, - { } -}; - -static int cx_auto_mux_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - - return snd_hda_input_mux_info(&spec->private_imux, uinfo); -} - -static int cx_auto_mux_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - - ucontrol->value.enumerated.item[0] = spec->cur_mux[0]; - return 0; -} - -/* look for the route the given pin from mux and return the index; - * if do_select is set, actually select the route. - */ -static int __select_input_connection(struct hda_codec *codec, hda_nid_t mux, - hda_nid_t pin, hda_nid_t *srcp, - bool do_select, int depth) -{ - hda_nid_t conn[HDA_MAX_NUM_INPUTS]; - int i, nums; - - switch (get_wcaps_type(get_wcaps(codec, mux))) { - case AC_WID_AUD_IN: - case AC_WID_AUD_SEL: - case AC_WID_AUD_MIX: - break; - default: - return -1; - } - - nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); - for (i = 0; i < nums; i++) - if (conn[i] == pin) { - if (do_select) - snd_hda_codec_write(codec, mux, 0, - AC_VERB_SET_CONNECT_SEL, i); - if (srcp) - *srcp = mux; - return i; - } - depth++; - if (depth == 2) - return -1; - for (i = 0; i < nums; i++) { - int ret = __select_input_connection(codec, conn[i], pin, srcp, - do_select, depth); - if (ret >= 0) { - if (do_select) - snd_hda_codec_write(codec, mux, 0, - AC_VERB_SET_CONNECT_SEL, i); - return i; - } - } - return -1; -} - -static void select_input_connection(struct hda_codec *codec, hda_nid_t mux, - hda_nid_t pin) -{ - __select_input_connection(codec, mux, pin, NULL, true, 0); -} - -static int get_input_connection(struct hda_codec *codec, hda_nid_t mux, - hda_nid_t pin) -{ - return __select_input_connection(codec, mux, pin, NULL, false, 0); -} - -static int cx_auto_mux_enum_update(struct hda_codec *codec, - const struct hda_input_mux *imux, - unsigned int idx) -{ - struct conexant_spec *spec = codec->spec; - hda_nid_t adc; - int changed = 1; - - if (!imux->num_items) - return 0; - if (idx >= imux->num_items) - idx = imux->num_items - 1; - if (spec->cur_mux[0] == idx) - changed = 0; - adc = spec->imux_info[idx].adc; - select_input_connection(codec, spec->imux_info[idx].adc, - spec->imux_info[idx].pin); - if (spec->cur_adc && spec->cur_adc != adc) { - /* stream is running, let's swap the current ADC */ - __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); - spec->cur_adc = adc; - snd_hda_codec_setup_stream(codec, adc, - spec->cur_adc_stream_tag, 0, - spec->cur_adc_format); - } - spec->cur_mux[0] = idx; - return changed; -} - -static int cx_auto_mux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - - return cx_auto_mux_enum_update(codec, &spec->private_imux, - ucontrol->value.enumerated.item[0]); -} - -static const struct snd_kcontrol_new cx_auto_capture_mixers[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = cx_auto_mux_enum_info, - .get = cx_auto_mux_enum_get, - .put = cx_auto_mux_enum_put - }, - {} -}; - -static bool select_automic(struct hda_codec *codec, int idx, bool detect) -{ - struct conexant_spec *spec = codec->spec; - if (idx < 0) - return false; - if (detect && !snd_hda_jack_detect(codec, spec->imux_info[idx].pin)) - return false; - cx_auto_mux_enum_update(codec, &spec->private_imux, idx); - return true; -} - -/* automatic switch internal and external mic */ -static void cx_auto_automic(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - - if (!spec->auto_mic) - return; - if (!select_automic(codec, spec->auto_mic_ext, true)) - if (!select_automic(codec, spec->auto_mic_dock, true)) - select_automic(codec, spec->auto_mic_int, false); -} - -static void cx_auto_unsol_event(struct hda_codec *codec, unsigned int res) -{ - switch (snd_hda_jack_get_action(codec, res >> 26)) { - case CONEXANT_HP_EVENT: - cx_auto_hp_automute(codec); - break; - case CONEXANT_LINE_EVENT: - cx_auto_line_automute(codec); - break; - case CONEXANT_MIC_EVENT: - cx_auto_automic(codec); - break; - } - snd_hda_jack_report_sync(codec); -} - -/* check whether the pin config is suitable for auto-mic switching; - * auto-mic is enabled only when one int-mic and one ext- and/or - * one dock-mic exist - */ -static void cx_auto_check_auto_mic(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - int pset[INPUT_PIN_ATTR_NORMAL + 1]; - int i; - - for (i = 0; i < ARRAY_SIZE(pset); i++) - pset[i] = -1; - for (i = 0; i < spec->private_imux.num_items; i++) { - hda_nid_t pin = spec->imux_info[i].pin; - unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin); - int type, attr; - attr = snd_hda_get_input_pin_attr(def_conf); - if (attr == INPUT_PIN_ATTR_UNUSED) - return; /* invalid entry */ - if (attr > INPUT_PIN_ATTR_NORMAL) - attr = INPUT_PIN_ATTR_NORMAL; - if (attr != INPUT_PIN_ATTR_INT && - !is_jack_detectable(codec, pin)) - return; /* non-detectable pin */ - type = get_defcfg_device(def_conf); - if (type != AC_JACK_MIC_IN && - (attr != INPUT_PIN_ATTR_DOCK || type != AC_JACK_LINE_IN)) - return; /* no valid input type */ - if (pset[attr] >= 0) - return; /* already occupied */ - pset[attr] = i; - } - if (pset[INPUT_PIN_ATTR_INT] < 0 || - (pset[INPUT_PIN_ATTR_NORMAL] < 0 && pset[INPUT_PIN_ATTR_DOCK])) - return; /* no input to switch*/ - spec->auto_mic = 1; - spec->auto_mic_ext = pset[INPUT_PIN_ATTR_NORMAL]; - spec->auto_mic_dock = pset[INPUT_PIN_ATTR_DOCK]; - spec->auto_mic_int = pset[INPUT_PIN_ATTR_INT]; -} - -static void cx_auto_parse_input(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - struct hda_input_mux *imux; - int i, j; - - imux = &spec->private_imux; - for (i = 0; i < cfg->num_inputs; i++) { - for (j = 0; j < spec->num_adc_nids; j++) { - hda_nid_t adc = spec->adc_nids[j]; - int idx = get_input_connection(codec, adc, - cfg->inputs[i].pin); - if (idx >= 0) { - const char *label; - label = hda_get_autocfg_input_label(codec, cfg, i); - spec->imux_info[imux->num_items].index = i; - spec->imux_info[imux->num_items].boost = 0; - spec->imux_info[imux->num_items].adc = adc; - spec->imux_info[imux->num_items].pin = - cfg->inputs[i].pin; - snd_hda_add_imux_item(imux, label, idx, NULL); - break; - } - } - } - if (imux->num_items >= 2 && cfg->num_inputs == imux->num_items) - cx_auto_check_auto_mic(codec); - if (imux->num_items > 1) { - for (i = 1; i < imux->num_items; i++) { - if (spec->imux_info[i].adc != spec->imux_info[0].adc) { - spec->adc_switching = 1; - break; - } - } - } -} - -/* get digital-input audio widget corresponding to the given pin */ -static hda_nid_t cx_auto_get_dig_in(struct hda_codec *codec, hda_nid_t pin) -{ - 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 wcaps = get_wcaps(codec, nid); - unsigned int type = get_wcaps_type(wcaps); - if (type == AC_WID_AUD_IN && (wcaps & AC_WCAP_DIGITAL)) { - if (get_connection_index(codec, nid, pin) >= 0) - return nid; - } - } - return 0; -} - -static void cx_auto_parse_digital(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t nid; - - if (cfg->dig_outs && - snd_hda_get_connections(codec, cfg->dig_out_pins[0], &nid, 1) == 1) - spec->multiout.dig_out_nid = nid; - if (cfg->dig_in_pin) - spec->dig_in_nid = cx_auto_get_dig_in(codec, cfg->dig_in_pin); -} - -#ifdef CONFIG_SND_HDA_INPUT_BEEP -static void cx_auto_parse_beep(struct hda_codec *codec) -{ - struct conexant_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++) - if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) { - set_beep_amp(spec, nid, 0, HDA_OUTPUT); - break; - } -} -#else -#define cx_auto_parse_beep(codec) -#endif - -/* parse EAPDs */ -static void cx_auto_parse_eapd(struct hda_codec *codec) -{ - struct conexant_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++) { - if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) - continue; - if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) - continue; - spec->eapds[spec->num_eapds++] = nid; - if (spec->num_eapds >= ARRAY_SIZE(spec->eapds)) - break; - } - - /* NOTE: below is a wild guess; if we have more than two EAPDs, - * it's a new chip, where EAPDs are supposed to be associated to - * pins, and we can control EAPD per pin. - * OTOH, if only one or two EAPDs are found, it's an old chip, - * thus it might control over all pins. - */ - spec->pin_eapd_ctrls = spec->num_eapds > 2; -} - -static int cx_auto_parse_auto_config(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - int err; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - - cx_auto_parse_output(codec); - cx_auto_parse_input(codec); - cx_auto_parse_digital(codec); - cx_auto_parse_beep(codec); - cx_auto_parse_eapd(codec); - return 0; -} - -static void cx_auto_turn_eapd(struct hda_codec *codec, int num_pins, - hda_nid_t *pins, bool on) -{ - int i; - for (i = 0; i < num_pins; i++) { - if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD) - snd_hda_codec_write(codec, pins[i], 0, - AC_VERB_SET_EAPD_BTLENABLE, - on ? 0x02 : 0); - } -} - -static void select_connection(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t src) -{ - int idx = get_connection_index(codec, pin, src); - if (idx >= 0) - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_CONNECT_SEL, idx); -} - -static void mute_outputs(struct hda_codec *codec, int num_nids, - const hda_nid_t *nids) -{ - int i, val; - - for (i = 0; i < num_nids; i++) { - hda_nid_t nid = nids[i]; - if (!(get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) - continue; - if (query_amp_caps(codec, nid, HDA_OUTPUT) & AC_AMPCAP_MUTE) - val = AMP_OUT_MUTE; - else - val = AMP_OUT_ZERO; - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, val); - } -} - -static void enable_unsol_pins(struct hda_codec *codec, int num_pins, - hda_nid_t *pins, unsigned int action) -{ - int i; - for (i = 0; i < num_pins; i++) - snd_hda_jack_detect_enable(codec, pins[i], action); -} - -static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) -{ - int i; - for (i = 0; i < nums; i++) - if (list[i] == nid) - return true; - return false; -} - -/* is the given NID found in any of autocfg items? */ -static bool found_in_autocfg(struct auto_pin_cfg *cfg, hda_nid_t nid) -{ - int i; - - if (found_in_nid_list(nid, cfg->line_out_pins, cfg->line_outs) || - found_in_nid_list(nid, cfg->hp_pins, cfg->hp_outs) || - found_in_nid_list(nid, cfg->speaker_pins, cfg->speaker_outs) || - found_in_nid_list(nid, cfg->dig_out_pins, cfg->dig_outs)) - return true; - for (i = 0; i < cfg->num_inputs; i++) - if (cfg->inputs[i].pin == nid) - return true; - if (cfg->dig_in_pin == nid) - return true; - return false; -} - -/* clear unsol-event tags on unused pins; Conexant codecs seem to leave - * invalid unsol tags by some reason - */ -static void clear_unsol_on_unused_pins(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - for (i = 0; i < codec->init_pins.used; i++) { - struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); - if (!found_in_autocfg(cfg, pin->nid)) - snd_hda_codec_write(codec, pin->nid, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, 0); - } -} - -/* turn on/off EAPD according to Master switch */ -static void cx_auto_vmaster_hook(void *private_data, int enabled) -{ - struct hda_codec *codec = private_data; - struct conexant_spec *spec = codec->spec; - - if (enabled && spec->pin_eapd_ctrls) { - cx_auto_update_speakers(codec); - return; - } - cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, enabled); -} - -static void cx_auto_init_output(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t nid; - int i; - - mute_outputs(codec, spec->multiout.num_dacs, spec->multiout.dac_nids); - for (i = 0; i < cfg->hp_outs; i++) { - unsigned int val = PIN_OUT; - if (snd_hda_query_pin_caps(codec, cfg->hp_pins[i]) & - AC_PINCAP_HP_DRV) - val |= AC_PINCTL_HP_EN; - snd_hda_codec_write(codec, cfg->hp_pins[i], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, val); - } - mute_outputs(codec, cfg->hp_outs, cfg->hp_pins); - mute_outputs(codec, cfg->line_outs, cfg->line_out_pins); - mute_outputs(codec, cfg->speaker_outs, cfg->speaker_pins); - for (i = 0; i < spec->dac_info_filled; i++) { - nid = spec->dac_info[i].dac; - if (!nid) - nid = spec->multiout.dac_nids[0]; - else if (nid & DAC_SLAVE_FLAG) - nid &= ~DAC_SLAVE_FLAG; - select_connection(codec, spec->dac_info[i].pin, nid); - } - if (spec->auto_mute) { - enable_unsol_pins(codec, cfg->hp_outs, cfg->hp_pins, - CONEXANT_HP_EVENT); - spec->hp_present = detect_jacks(codec, cfg->hp_outs, - cfg->hp_pins); - if (spec->detect_line) { - enable_unsol_pins(codec, cfg->line_outs, - cfg->line_out_pins, - CONEXANT_LINE_EVENT); - spec->line_present = - detect_jacks(codec, cfg->line_outs, - cfg->line_out_pins); - } - } - cx_auto_update_speakers(codec); - /* turn on all EAPDs if no individual EAPD control is available */ - if (!spec->pin_eapd_ctrls) - cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true); - clear_unsol_on_unused_pins(codec); -} - -static void cx_auto_init_input(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i, val; - - for (i = 0; i < spec->num_adc_nids; i++) { - hda_nid_t nid = spec->adc_nids[i]; - if (!(get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) - continue; - if (query_amp_caps(codec, nid, HDA_INPUT) & AC_AMPCAP_MUTE) - val = AMP_IN_MUTE(0); - else - val = AMP_IN_UNMUTE(0); - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - val); - } - - for (i = 0; i < cfg->num_inputs; i++) { - unsigned int type; - if (cfg->inputs[i].type == AUTO_PIN_MIC) - type = PIN_VREF80; - else - type = PIN_IN; - snd_hda_codec_write(codec, cfg->inputs[i].pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, type); - } - - if (spec->auto_mic) { - if (spec->auto_mic_ext >= 0) { - snd_hda_jack_detect_enable(codec, - cfg->inputs[spec->auto_mic_ext].pin, - CONEXANT_MIC_EVENT); - } - if (spec->auto_mic_dock >= 0) { - snd_hda_jack_detect_enable(codec, - cfg->inputs[spec->auto_mic_dock].pin, - CONEXANT_MIC_EVENT); - } - cx_auto_automic(codec); - } else { - select_input_connection(codec, spec->imux_info[0].adc, - spec->imux_info[0].pin); - } -} - -static void cx_auto_init_digital(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - - if (spec->multiout.dig_out_nid) - snd_hda_codec_write(codec, cfg->dig_out_pins[0], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - if (spec->dig_in_nid) - snd_hda_codec_write(codec, cfg->dig_in_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); -} - -static int cx_auto_init(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - /*snd_hda_sequence_write(codec, cx_auto_init_verbs);*/ - cx_auto_init_output(codec); - cx_auto_init_input(codec); - cx_auto_init_digital(codec); - snd_hda_jack_report_sync(codec); - snd_hda_sync_vmaster_hook(&spec->vmaster_mute); - return 0; -} - -static int cx_auto_add_volume_idx(struct hda_codec *codec, const char *basename, - const char *dir, int cidx, - hda_nid_t nid, int hda_dir, int amp_idx) -{ - static char name[32]; - static struct snd_kcontrol_new knew[] = { - HDA_CODEC_VOLUME(name, 0, 0, 0), - HDA_CODEC_MUTE(name, 0, 0, 0), - }; - static const char * const sfx[2] = { "Volume", "Switch" }; - int i, err; - - for (i = 0; i < 2; i++) { - struct snd_kcontrol *kctl; - knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, 3, amp_idx, - hda_dir); - knew[i].subdevice = HDA_SUBDEV_AMP_FLAG; - knew[i].index = cidx; - snprintf(name, sizeof(name), "%s%s %s", basename, dir, sfx[i]); - kctl = snd_ctl_new1(&knew[i], codec); - if (!kctl) - return -ENOMEM; - err = snd_hda_ctl_add(codec, nid, kctl); - if (err < 0) - return err; - if (!(query_amp_caps(codec, nid, hda_dir) & - (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE))) - break; - } - return 0; -} - -#define cx_auto_add_volume(codec, str, dir, cidx, nid, hda_dir) \ - cx_auto_add_volume_idx(codec, str, dir, cidx, nid, hda_dir, 0) - -#define cx_auto_add_pb_volume(codec, nid, str, idx) \ - cx_auto_add_volume(codec, str, " Playback", idx, nid, HDA_OUTPUT) - -static int try_add_pb_volume(struct hda_codec *codec, hda_nid_t dac, - hda_nid_t pin, const char *name, int idx) -{ - unsigned int caps; - if (dac && !(dac & DAC_SLAVE_FLAG)) { - caps = query_amp_caps(codec, dac, HDA_OUTPUT); - if (caps & AC_AMPCAP_NUM_STEPS) - return cx_auto_add_pb_volume(codec, dac, name, idx); - } - caps = query_amp_caps(codec, pin, HDA_OUTPUT); - if (caps & AC_AMPCAP_NUM_STEPS) - return cx_auto_add_pb_volume(codec, pin, name, idx); - return 0; -} - -static int cx_auto_build_output_controls(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - int i, err; - int num_line = 0, num_hp = 0, num_spk = 0; - static const char * const texts[3] = { "Front", "Surround", "CLFE" }; - - if (spec->dac_info_filled == 1) - return try_add_pb_volume(codec, spec->dac_info[0].dac, - spec->dac_info[0].pin, - "Master", 0); - - for (i = 0; i < spec->dac_info_filled; i++) { - const char *label; - int idx, type; - hda_nid_t dac = spec->dac_info[i].dac; - type = spec->dac_info[i].type; - if (type == AUTO_PIN_LINE_OUT) - type = spec->autocfg.line_out_type; - switch (type) { - case AUTO_PIN_LINE_OUT: - default: - label = texts[num_line++]; - idx = 0; - break; - case AUTO_PIN_HP_OUT: - label = "Headphone"; - idx = num_hp++; - break; - case AUTO_PIN_SPEAKER_OUT: - label = "Speaker"; - idx = num_spk++; - break; - } - err = try_add_pb_volume(codec, dac, - spec->dac_info[i].pin, - label, idx); - if (err < 0) - return err; - } - - if (spec->auto_mute) { - err = snd_hda_add_new_ctls(codec, cx_automute_mode_enum); - if (err < 0) - return err; - } - - return 0; -} - -static int cx_auto_add_capture_volume(struct hda_codec *codec, hda_nid_t nid, - const char *label, const char *pfx, - int cidx) -{ - struct conexant_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->num_adc_nids; i++) { - hda_nid_t adc_nid = spec->adc_nids[i]; - int idx = get_input_connection(codec, adc_nid, nid); - if (idx < 0) - continue; - if (codec->single_adc_amp) - idx = 0; - return cx_auto_add_volume_idx(codec, label, pfx, - cidx, adc_nid, HDA_INPUT, idx); - } - return 0; -} - -static int cx_auto_add_boost_volume(struct hda_codec *codec, int idx, - const char *label, int cidx) -{ - struct conexant_spec *spec = codec->spec; - hda_nid_t mux, nid; - int i, con; - - nid = spec->imux_info[idx].pin; - if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) - return cx_auto_add_volume(codec, label, " Boost", cidx, - nid, HDA_INPUT); - con = __select_input_connection(codec, spec->imux_info[idx].adc, nid, - &mux, false, 0); - if (con < 0) - return 0; - for (i = 0; i < idx; i++) { - if (spec->imux_info[i].boost == mux) - return 0; /* already present */ - } - - if (get_wcaps(codec, mux) & AC_WCAP_OUT_AMP) { - spec->imux_info[idx].boost = mux; - return cx_auto_add_volume(codec, label, " Boost", 0, - mux, HDA_OUTPUT); - } - return 0; -} - -static int cx_auto_build_input_controls(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux; - const char *prev_label; - int input_conn[HDA_MAX_NUM_INPUTS]; - int i, j, err, cidx; - int multi_connection; - - if (!imux->num_items) - return 0; - - multi_connection = 0; - for (i = 0; i < imux->num_items; i++) { - cidx = get_input_connection(codec, spec->imux_info[i].adc, - spec->imux_info[i].pin); - if (cidx < 0) - continue; - input_conn[i] = spec->imux_info[i].adc; - if (!codec->single_adc_amp) - input_conn[i] |= cidx << 8; - if (i > 0 && input_conn[i] != input_conn[0]) - multi_connection = 1; - } - - prev_label = NULL; - cidx = 0; - for (i = 0; i < imux->num_items; i++) { - hda_nid_t nid = spec->imux_info[i].pin; - const char *label; - - label = hda_get_autocfg_input_label(codec, &spec->autocfg, - spec->imux_info[i].index); - if (label == prev_label) - cidx++; - else - cidx = 0; - prev_label = label; - - err = cx_auto_add_boost_volume(codec, i, label, cidx); - if (err < 0) - return err; - - if (!multi_connection) { - if (i > 0) - continue; - err = cx_auto_add_capture_volume(codec, nid, - "Capture", "", cidx); - } else { - bool dup_found = false; - for (j = 0; j < i; j++) { - if (input_conn[j] == input_conn[i]) { - dup_found = true; - break; - } - } - if (dup_found) - continue; - err = cx_auto_add_capture_volume(codec, nid, - label, " Capture", cidx); - } - if (err < 0) - return err; - } - - if (spec->private_imux.num_items > 1 && !spec->auto_mic) { - err = snd_hda_add_new_ctls(codec, cx_auto_capture_mixers); - if (err < 0) - return err; - } - - return 0; -} - -static int cx_auto_build_controls(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - int err; - - err = cx_auto_build_output_controls(codec); - if (err < 0) - return err; - err = cx_auto_build_input_controls(codec); - if (err < 0) - return err; - err = conexant_build_controls(codec); - if (err < 0) - return err; - err = snd_hda_jack_add_kctls(codec, &spec->autocfg); - if (err < 0) - return err; - if (spec->vmaster_mute.sw_kctl) { - spec->vmaster_mute.hook = cx_auto_vmaster_hook; - err = snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, - spec->vmaster_mute_led); - if (err < 0) - return err; - } - return 0; -} - -static int cx_auto_search_adcs(struct hda_codec *codec) -{ - struct conexant_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 caps = get_wcaps(codec, nid); - if (get_wcaps_type(caps) != AC_WID_AUD_IN) - continue; - if (caps & AC_WCAP_DIGITAL) - continue; - if (snd_BUG_ON(spec->num_adc_nids >= - ARRAY_SIZE(spec->private_adc_nids))) - break; - spec->private_adc_nids[spec->num_adc_nids++] = nid; - } - spec->adc_nids = spec->private_adc_nids; - return 0; -} - -static const struct hda_codec_ops cx_auto_patch_ops = { - .build_controls = cx_auto_build_controls, - .build_pcms = conexant_build_pcms, - .init = cx_auto_init, - .free = conexant_free, - .unsol_event = cx_auto_unsol_event, -#ifdef CONFIG_SND_HDA_POWER_SAVE - .suspend = conexant_suspend, -#endif - .reboot_notify = snd_hda_shutup_pins, -}; - -/* - * pin fix-up - */ -struct cxt_pincfg { - hda_nid_t nid; - u32 val; -}; - -static void apply_pincfg(struct hda_codec *codec, const struct cxt_pincfg *cfg) -{ - for (; cfg->nid; cfg++) - snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val); - -} - -static void apply_pin_fixup(struct hda_codec *codec, - const struct snd_pci_quirk *quirk, - const struct cxt_pincfg **table) -{ - quirk = snd_pci_quirk_lookup(codec->bus->pci, quirk); - if (quirk) { - snd_printdd(KERN_INFO "hda_codec: applying pincfg for %s\n", - quirk->name); - apply_pincfg(codec, table[quirk->value]); - } -} - -enum { - CXT_PINCFG_LENOVO_X200, - CXT_PINCFG_LENOVO_TP410, -}; - -/* ThinkPad X200 & co with cxt5051 */ -static const struct cxt_pincfg cxt_pincfg_lenovo_x200[] = { - { 0x16, 0x042140ff }, /* HP (seq# overridden) */ - { 0x17, 0x21a11000 }, /* dock-mic */ - { 0x19, 0x2121103f }, /* dock-HP */ - { 0x1c, 0x21440100 }, /* dock SPDIF out */ - {} -}; - -/* ThinkPad 410/420/510/520, X201 & co with cxt5066 */ -static const struct cxt_pincfg cxt_pincfg_lenovo_tp410[] = { - { 0x19, 0x042110ff }, /* HP (seq# overridden) */ - { 0x1a, 0x21a190f0 }, /* dock-mic */ - { 0x1c, 0x212140ff }, /* dock-HP */ - {} -}; - -static const struct cxt_pincfg *cxt_pincfg_tbl[] = { - [CXT_PINCFG_LENOVO_X200] = cxt_pincfg_lenovo_x200, - [CXT_PINCFG_LENOVO_TP410] = cxt_pincfg_lenovo_tp410, -}; - -static const struct snd_pci_quirk cxt5051_fixups[] = { - SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT_PINCFG_LENOVO_X200), - {} -}; - -static const struct snd_pci_quirk cxt5066_fixups[] = { - SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410), - SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo T410", CXT_PINCFG_LENOVO_TP410), - SND_PCI_QUIRK(0x17aa, 0x215f, "Lenovo T510", CXT_PINCFG_LENOVO_TP410), - SND_PCI_QUIRK(0x17aa, 0x21ce, "Lenovo T420", CXT_PINCFG_LENOVO_TP410), - SND_PCI_QUIRK(0x17aa, 0x21cf, "Lenovo T520", CXT_PINCFG_LENOVO_TP410), - {} -}; - -/* add "fake" mute amp-caps to DACs on cx5051 so that mixer mute switches - * can be created (bko#42825) - */ -static void add_cx5051_fake_mutes(struct hda_codec *codec) -{ - static hda_nid_t out_nids[] = { - 0x10, 0x11, 0 - }; - hda_nid_t *p; - - for (p = out_nids; *p; p++) - snd_hda_override_amp_caps(codec, *p, HDA_OUTPUT, - AC_AMPCAP_MIN_MUTE | - query_amp_caps(codec, *p, HDA_OUTPUT)); -} - -static int patch_conexant_auto(struct hda_codec *codec) -{ - struct conexant_spec *spec; - int err; - - printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return -ENOMEM; - codec->spec = spec; - - switch (codec->vendor_id) { - case 0x14f15045: - codec->single_adc_amp = 1; - break; - case 0x14f15051: - add_cx5051_fake_mutes(codec); - codec->pin_amp_workaround = 1; - apply_pin_fixup(codec, cxt5051_fixups, cxt_pincfg_tbl); - break; - default: - codec->pin_amp_workaround = 1; - apply_pin_fixup(codec, cxt5066_fixups, cxt_pincfg_tbl); - } - - /* Show mute-led control only on HP laptops - * This is a sort of white-list: on HP laptops, EAPD corresponds - * only to the mute-LED without actualy amp function. Meanwhile, - * others may use EAPD really as an amp switch, so it might be - * not good to expose it blindly. - */ - switch (codec->subsystem_id >> 16) { - case 0x103c: - spec->vmaster_mute_led = 1; - break; - } - - err = cx_auto_search_adcs(codec); - if (err < 0) - return err; - err = cx_auto_parse_auto_config(codec); - if (err < 0) { - kfree(codec->spec); - codec->spec = NULL; - return err; - } - spec->capture_stream = &cx_auto_pcm_analog_capture; - codec->patch_ops = cx_auto_patch_ops; - if (spec->beep_amp) - snd_hda_attach_beep_device(codec, spec->beep_amp); - - /* Some laptops with Conexant chips show stalls in S3 resume, - * which falls into the single-cmd mode. - * Better to make reset, then. - */ - if (!codec->bus->sync_write) { - snd_printd("hda_codec: " - "Enable sync_write for stable communication\n"); - codec->bus->sync_write = 1; - codec->bus->allow_bus_reset = 1; - } - - return 0; -} - -/* - */ - -static const struct hda_codec_preset snd_hda_preset_conexant[] = { - { .id = 0x14f15045, .name = "CX20549 (Venice)", - .patch = patch_cxt5045 }, - { .id = 0x14f15047, .name = "CX20551 (Waikiki)", - .patch = patch_cxt5047 }, - { .id = 0x14f15051, .name = "CX20561 (Hermosa)", - .patch = patch_cxt5051 }, - { .id = 0x14f15066, .name = "CX20582 (Pebble)", - .patch = patch_cxt5066 }, - { .id = 0x14f15067, .name = "CX20583 (Pebble HSF)", - .patch = patch_cxt5066 }, - { .id = 0x14f15068, .name = "CX20584", - .patch = patch_cxt5066 }, - { .id = 0x14f15069, .name = "CX20585", - .patch = patch_cxt5066 }, - { .id = 0x14f1506c, .name = "CX20588", - .patch = patch_cxt5066 }, - { .id = 0x14f1506e, .name = "CX20590", - .patch = patch_cxt5066 }, - { .id = 0x14f15097, .name = "CX20631", - .patch = patch_conexant_auto }, - { .id = 0x14f15098, .name = "CX20632", - .patch = patch_conexant_auto }, - { .id = 0x14f150a1, .name = "CX20641", - .patch = patch_conexant_auto }, - { .id = 0x14f150a2, .name = "CX20642", - .patch = patch_conexant_auto }, - { .id = 0x14f150ab, .name = "CX20651", - .patch = patch_conexant_auto }, - { .id = 0x14f150ac, .name = "CX20652", - .patch = patch_conexant_auto }, - { .id = 0x14f150b8, .name = "CX20664", - .patch = patch_conexant_auto }, - { .id = 0x14f150b9, .name = "CX20665", - .patch = patch_conexant_auto }, - {} /* terminator */ -}; - -MODULE_ALIAS("snd-hda-codec-id:14f15045"); -MODULE_ALIAS("snd-hda-codec-id:14f15047"); -MODULE_ALIAS("snd-hda-codec-id:14f15051"); -MODULE_ALIAS("snd-hda-codec-id:14f15066"); -MODULE_ALIAS("snd-hda-codec-id:14f15067"); -MODULE_ALIAS("snd-hda-codec-id:14f15068"); -MODULE_ALIAS("snd-hda-codec-id:14f15069"); -MODULE_ALIAS("snd-hda-codec-id:14f1506c"); -MODULE_ALIAS("snd-hda-codec-id:14f1506e"); -MODULE_ALIAS("snd-hda-codec-id:14f15097"); -MODULE_ALIAS("snd-hda-codec-id:14f15098"); -MODULE_ALIAS("snd-hda-codec-id:14f150a1"); -MODULE_ALIAS("snd-hda-codec-id:14f150a2"); -MODULE_ALIAS("snd-hda-codec-id:14f150ab"); -MODULE_ALIAS("snd-hda-codec-id:14f150ac"); -MODULE_ALIAS("snd-hda-codec-id:14f150b8"); -MODULE_ALIAS("snd-hda-codec-id:14f150b9"); - -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/ANDROID_3.4.5/sound/pci/hda/patch_hdmi.c b/ANDROID_3.4.5/sound/pci/hda/patch_hdmi.c deleted file mode 100644 index 83f345f3..00000000 --- a/ANDROID_3.4.5/sound/pci/hda/patch_hdmi.c +++ /dev/null @@ -1,1986 +0,0 @@ -/* - * - * patch_hdmi.c - routines for HDMI/DisplayPort codecs - * - * Copyright(c) 2008-2010 Intel Corporation. All rights reserved. - * Copyright (c) 2006 ATI Technologies Inc. - * Copyright (c) 2008 NVIDIA Corp. All rights reserved. - * Copyright (c) 2008 Wei Ni <wni@nvidia.com> - * - * Authors: - * 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 <linux/module.h> -#include <sound/core.h> -#include <sound/jack.h> -#include "hda_codec.h" -#include "hda_local.h" -#include "hda_jack.h" - -static bool static_hdmi_pcm; -module_param(static_hdmi_pcm, bool, 0644); -MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info"); - -/* - * The HDMI/DisplayPort configuration can be highly dynamic. A graphics device - * could support N independent pipes, each of them can be connected to one or - * more ports (DVI, HDMI or DisplayPort). - * - * The HDA correspondence of pipes/ports are converter/pin nodes. - */ -#define MAX_HDMI_CVTS 8 -#define MAX_HDMI_PINS 8 - -struct hdmi_spec_per_cvt { - hda_nid_t cvt_nid; - int assigned; - unsigned int channels_min; - unsigned int channels_max; - u32 rates; - u64 formats; - unsigned int maxbps; -}; - -struct hdmi_spec_per_pin { - hda_nid_t pin_nid; - int num_mux_nids; - hda_nid_t mux_nids[HDA_MAX_CONNECTIONS]; - - struct hda_codec *codec; - struct hdmi_eld sink_eld; - struct delayed_work work; - int repoll_count; -}; - -struct hdmi_spec { - int num_cvts; - struct hdmi_spec_per_cvt cvts[MAX_HDMI_CVTS]; - - int num_pins; - struct hdmi_spec_per_pin pins[MAX_HDMI_PINS]; - struct hda_pcm pcm_rec[MAX_HDMI_PINS]; - - /* - * Non-generic ATI/NVIDIA specific - */ - struct hda_multi_out multiout; - const struct hda_pcm_stream *pcm_playback; -}; - - -struct hdmi_audio_infoframe { - u8 type; /* 0x84 */ - u8 ver; /* 0x01 */ - u8 len; /* 0x0a */ - - u8 checksum; - - u8 CC02_CT47; /* CC in bits 0:2, CT in 4:7 */ - u8 SS01_SF24; - u8 CXT04; - u8 CA; - u8 LFEPBL01_LSV36_DM_INH7; -}; - -struct dp_audio_infoframe { - u8 type; /* 0x84 */ - u8 len; /* 0x1b */ - u8 ver; /* 0x11 << 2 */ - - u8 CC02_CT47; /* match with HDMI infoframe from this on */ - u8 SS01_SF24; - u8 CXT04; - u8 CA; - u8 LFEPBL01_LSV36_DM_INH7; -}; - -union audio_infoframe { - struct hdmi_audio_infoframe hdmi; - struct dp_audio_infoframe dp; - u8 bytes[0]; -}; - -/* - * CEA speaker placement: - * - * FLH FCH FRH - * FLW FL FLC FC FRC FR FRW - * - * LFE - * TC - * - * RL RLC RC RRC RR - * - * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to - * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC. - */ -enum cea_speaker_placement { - FL = (1 << 0), /* Front Left */ - FC = (1 << 1), /* Front Center */ - FR = (1 << 2), /* Front Right */ - FLC = (1 << 3), /* Front Left Center */ - FRC = (1 << 4), /* Front Right Center */ - RL = (1 << 5), /* Rear Left */ - RC = (1 << 6), /* Rear Center */ - RR = (1 << 7), /* Rear Right */ - RLC = (1 << 8), /* Rear Left Center */ - RRC = (1 << 9), /* Rear Right Center */ - LFE = (1 << 10), /* Low Frequency Effect */ - FLW = (1 << 11), /* Front Left Wide */ - FRW = (1 << 12), /* Front Right Wide */ - FLH = (1 << 13), /* Front Left High */ - FCH = (1 << 14), /* Front Center High */ - FRH = (1 << 15), /* Front Right High */ - TC = (1 << 16), /* Top Center */ -}; - -/* - * ELD SA bits in the CEA Speaker Allocation data block - */ -static int eld_speaker_allocation_bits[] = { - [0] = FL | FR, - [1] = LFE, - [2] = FC, - [3] = RL | RR, - [4] = RC, - [5] = FLC | FRC, - [6] = RLC | RRC, - /* the following are not defined in ELD yet */ - [7] = FLW | FRW, - [8] = FLH | FRH, - [9] = TC, - [10] = FCH, -}; - -struct cea_channel_speaker_allocation { - int ca_index; - int speakers[8]; - - /* derived values, just for convenience */ - int channels; - int spk_mask; -}; - -/* - * ALSA sequence is: - * - * surround40 surround41 surround50 surround51 surround71 - * ch0 front left = = = = - * ch1 front right = = = = - * ch2 rear left = = = = - * ch3 rear right = = = = - * ch4 LFE center center center - * ch5 LFE LFE - * ch6 side left - * ch7 side right - * - * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR} - */ -static int hdmi_channel_mapping[0x32][8] = { - /* stereo */ - [0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 }, - /* 2.1 */ - [0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 }, - /* Dolby Surround */ - [0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 }, - /* surround40 */ - [0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 }, - /* 4ch */ - [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 }, - /* surround41 */ - [0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 }, - /* surround50 */ - [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 }, - /* surround51 */ - [0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 }, - /* 7.1 */ - [0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 }, -}; - -/* - * This is an ordered list! - * - * The preceding ones have better chances to be selected by - * hdmi_channel_allocation(). - */ -static struct cea_channel_speaker_allocation channel_allocations[] = { -/* channel: 7 6 5 4 3 2 1 0 */ -{ .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } }, - /* 2.1 */ -{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } }, - /* Dolby Surround */ -{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } }, - /* surround40 */ -{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } }, - /* surround41 */ -{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } }, - /* surround50 */ -{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } }, - /* surround51 */ -{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } }, - /* 6.1 */ -{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } }, - /* surround71 */ -{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } }, - -{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } }, -{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } }, -{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } }, -{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } }, -{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } }, -{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } }, -{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } }, -{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } }, -{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } }, -{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } }, -{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } }, -{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } }, -{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } }, -{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x20, .speakers = { 0, FCH, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x21, .speakers = { 0, FCH, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x22, .speakers = { TC, 0, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x23, .speakers = { TC, 0, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x29, .speakers = { TC, RC, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x2b, .speakers = { FCH, RC, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x2d, .speakers = { TC, FCH, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x2f, .speakers = { FRH, FLH, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } }, -}; - - -/* - * HDMI routines - */ - -static int pin_nid_to_pin_index(struct hdmi_spec *spec, hda_nid_t pin_nid) -{ - int pin_idx; - - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) - if (spec->pins[pin_idx].pin_nid == pin_nid) - return pin_idx; - - snd_printk(KERN_WARNING "HDMI: pin nid %d not registered\n", pin_nid); - return -EINVAL; -} - -static int hinfo_to_pin_index(struct hdmi_spec *spec, - struct hda_pcm_stream *hinfo) -{ - int pin_idx; - - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) - if (&spec->pcm_rec[pin_idx].stream[0] == hinfo) - return pin_idx; - - snd_printk(KERN_WARNING "HDMI: hinfo %p not registered\n", hinfo); - return -EINVAL; -} - -static int cvt_nid_to_cvt_index(struct hdmi_spec *spec, hda_nid_t cvt_nid) -{ - int cvt_idx; - - for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) - if (spec->cvts[cvt_idx].cvt_nid == cvt_nid) - return cvt_idx; - - snd_printk(KERN_WARNING "HDMI: cvt nid %d not registered\n", cvt_nid); - return -EINVAL; -} - -static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hdmi_spec *spec; - int pin_idx; - - spec = codec->spec; - uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; - - pin_idx = kcontrol->private_value; - uinfo->count = spec->pins[pin_idx].sink_eld.eld_size; - - return 0; -} - -static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hdmi_spec *spec; - int pin_idx; - - spec = codec->spec; - pin_idx = kcontrol->private_value; - - memcpy(ucontrol->value.bytes.data, - spec->pins[pin_idx].sink_eld.eld_buffer, ELD_MAX_SIZE); - - return 0; -} - -static struct snd_kcontrol_new eld_bytes_ctl = { - .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, - .iface = SNDRV_CTL_ELEM_IFACE_PCM, - .name = "ELD", - .info = hdmi_eld_ctl_info, - .get = hdmi_eld_ctl_get, -}; - -static int hdmi_create_eld_ctl(struct hda_codec *codec, int pin_idx, - int device) -{ - struct snd_kcontrol *kctl; - struct hdmi_spec *spec = codec->spec; - int err; - - kctl = snd_ctl_new1(&eld_bytes_ctl, codec); - if (!kctl) - return -ENOMEM; - kctl->private_value = pin_idx; - kctl->id.device = device; - - err = snd_hda_ctl_add(codec, spec->pins[pin_idx].pin_nid, kctl); - if (err < 0) - return err; - - return 0; -} - -#ifdef BE_PARANOID -static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t pin_nid, - int *packet_index, int *byte_index) -{ - int val; - - val = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_HDMI_DIP_INDEX, 0); - - *packet_index = val >> 5; - *byte_index = val & 0x1f; -} -#endif - -static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t pin_nid, - int packet_index, int byte_index) -{ - int val; - - val = (packet_index << 5) | (byte_index & 0x1f); - - snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val); -} - -static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid, - unsigned char val) -{ - snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val); -} - -static void hdmi_init_pin(struct hda_codec *codec, hda_nid_t pin_nid) -{ - /* Unmute */ - if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP) - snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - /* Disable pin out until stream is active*/ - snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0); -} - -static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t cvt_nid) -{ - 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, - hda_nid_t cvt_nid, int chs) -{ - if (chs != hdmi_get_channel_count(codec, cvt_nid)) - snd_hda_codec_write(codec, cvt_nid, 0, - AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); -} - - -/* - * Channel mapping routines - */ - -/* - * Compute derived values in channel_allocations[]. - */ -static void init_channel_allocations(void) -{ - int i, j; - struct cea_channel_speaker_allocation *p; - - for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { - p = channel_allocations + i; - p->channels = 0; - p->spk_mask = 0; - for (j = 0; j < ARRAY_SIZE(p->speakers); j++) - if (p->speakers[j]) { - p->channels++; - p->spk_mask |= p->speakers[j]; - } - } -} - -/* - * The transformation takes two steps: - * - * eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask - * spk_mask => (channel_allocations[]) => ai->CA - * - * TODO: it could select the wrong CA from multiple candidates. -*/ -static int hdmi_channel_allocation(struct hdmi_eld *eld, int channels) -{ - int i; - int ca = 0; - int spk_mask = 0; - char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; - - /* - * CA defaults to 0 for basic stereo audio - */ - 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) { - ca = channel_allocations[i].ca_index; - break; - } - } - - snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf)); - snd_printdd("HDMI: select CA 0x%x for %d-channel allocation: %s\n", - ca, channels, buf); - - return ca; -} - -static void hdmi_debug_channel_mapping(struct hda_codec *codec, - hda_nid_t pin_nid) -{ -#ifdef CONFIG_SND_DEBUG_VERBOSE - int i; - int slot; - - for (i = 0; i < 8; i++) { - slot = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_HDMI_CHAN_SLOT, i); - printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n", - slot >> 4, slot & 0xf); - } -#endif -} - - -static void hdmi_setup_channel_mapping(struct hda_codec *codec, - hda_nid_t pin_nid, - int ca) -{ - int i; - int err; - - if (hdmi_channel_mapping[ca][1] == 0) { - for (i = 0; i < channel_allocations[ca].channels; i++) - hdmi_channel_mapping[ca][i] = i | (i << 4); - for (; i < 8; i++) - hdmi_channel_mapping[ca][i] = 0xf | (i << 4); - } - - for (i = 0; i < 8; i++) { - err = snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_HDMI_CHAN_SLOT, - hdmi_channel_mapping[ca][i]); - if (err) { - snd_printdd(KERN_NOTICE - "HDMI: channel mapping failed\n"); - break; - } - } - - hdmi_debug_channel_mapping(codec, pin_nid); -} - - -/* - * Audio InfoFrame routines - */ - -/* - * Enable Audio InfoFrame Transmission - */ -static void hdmi_start_infoframe_trans(struct hda_codec *codec, - hda_nid_t pin_nid) -{ - hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); - snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, - AC_DIPXMIT_BEST); -} - -/* - * Disable Audio InfoFrame Transmission - */ -static void hdmi_stop_infoframe_trans(struct hda_codec *codec, - hda_nid_t pin_nid) -{ - hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); - snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, - AC_DIPXMIT_DISABLE); -} - -static void hdmi_debug_dip_size(struct hda_codec *codec, hda_nid_t pin_nid) -{ -#ifdef CONFIG_SND_DEBUG_VERBOSE - int i; - int size; - - size = snd_hdmi_get_eld_size(codec, pin_nid); - printk(KERN_DEBUG "HDMI: ELD buf size is %d\n", size); - - for (i = 0; i < 8; i++) { - size = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_HDMI_DIP_SIZE, i); - printk(KERN_DEBUG "HDMI: DIP GP[%d] buf size is %d\n", i, size); - } -#endif -} - -static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid) -{ -#ifdef BE_PARANOID - int i, j; - int size; - int pi, bi; - for (i = 0; i < 8; i++) { - size = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_HDMI_DIP_SIZE, i); - if (size == 0) - continue; - - hdmi_set_dip_index(codec, pin_nid, i, 0x0); - for (j = 1; j < 1000; j++) { - hdmi_write_dip_byte(codec, pin_nid, 0x0); - hdmi_get_dip_index(codec, pin_nid, &pi, &bi); - if (pi != i) - snd_printd(KERN_INFO "dip index %d: %d != %d\n", - bi, pi, i); - if (bi == 0) /* byte index wrapped around */ - break; - } - snd_printd(KERN_INFO - "HDMI: DIP GP[%d] buf reported size=%d, written=%d\n", - i, size, j); - } -#endif -} - -static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *hdmi_ai) -{ - u8 *bytes = (u8 *)hdmi_ai; - u8 sum = 0; - int i; - - hdmi_ai->checksum = 0; - - for (i = 0; i < sizeof(*hdmi_ai); i++) - sum += bytes[i]; - - hdmi_ai->checksum = -sum; -} - -static void hdmi_fill_audio_infoframe(struct hda_codec *codec, - hda_nid_t pin_nid, - u8 *dip, int size) -{ - int i; - - hdmi_debug_dip_size(codec, pin_nid); - hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */ - - hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); - for (i = 0; i < size; i++) - hdmi_write_dip_byte(codec, pin_nid, dip[i]); -} - -static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid, - u8 *dip, int size) -{ - u8 val; - int i; - - if (snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_DIP_XMIT, 0) - != AC_DIPXMIT_BEST) - return false; - - hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); - for (i = 0; i < size; i++) { - val = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_HDMI_DIP_DATA, 0); - if (val != dip[i]) - return false; - } - - return true; -} - -static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx, - struct snd_pcm_substream *substream) -{ - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; - hda_nid_t pin_nid = per_pin->pin_nid; - int channels = substream->runtime->channels; - struct hdmi_eld *eld; - int ca; - union audio_infoframe ai; - - eld = &spec->pins[pin_idx].sink_eld; - if (!eld->monitor_present) - return; - - ca = hdmi_channel_allocation(eld, channels); - - memset(&ai, 0, sizeof(ai)); - if (eld->conn_type == 0) { /* HDMI */ - struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi; - - hdmi_ai->type = 0x84; - hdmi_ai->ver = 0x01; - hdmi_ai->len = 0x0a; - hdmi_ai->CC02_CT47 = channels - 1; - hdmi_ai->CA = ca; - hdmi_checksum_audio_infoframe(hdmi_ai); - } else if (eld->conn_type == 1) { /* DisplayPort */ - struct dp_audio_infoframe *dp_ai = &ai.dp; - - dp_ai->type = 0x84; - dp_ai->len = 0x1b; - dp_ai->ver = 0x11 << 2; - dp_ai->CC02_CT47 = channels - 1; - dp_ai->CA = ca; - } else { - snd_printd("HDMI: unknown connection type at pin %d\n", - pin_nid); - return; - } - - /* - * sizeof(ai) is used instead of sizeof(*hdmi_ai) or - * sizeof(*dp_ai) to avoid partial match/update problems when - * the user switches between HDMI/DP monitors. - */ - if (!hdmi_infoframe_uptodate(codec, pin_nid, ai.bytes, - sizeof(ai))) { - snd_printdd("hdmi_setup_audio_infoframe: " - "pin=%d channels=%d\n", - pin_nid, - channels); - hdmi_setup_channel_mapping(codec, pin_nid, ca); - hdmi_stop_infoframe_trans(codec, pin_nid); - hdmi_fill_audio_infoframe(codec, pin_nid, - ai.bytes, sizeof(ai)); - hdmi_start_infoframe_trans(codec, pin_nid); - } -} - - -/* - * Unsolicited events - */ - -static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll); - -static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) -{ - struct hdmi_spec *spec = codec->spec; - int tag = res >> AC_UNSOL_RES_TAG_SHIFT; - int pin_nid; - int pin_idx; - struct hda_jack_tbl *jack; - - jack = snd_hda_jack_tbl_get_from_tag(codec, tag); - if (!jack) - return; - pin_nid = jack->nid; - jack->jack_dirty = 1; - - _snd_printd(SND_PR_VERBOSE, - "HDMI hot plug event: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n", - codec->addr, pin_nid, - !!(res & AC_UNSOL_RES_PD), !!(res & AC_UNSOL_RES_ELDV)); - - pin_idx = pin_nid_to_pin_index(spec, pin_nid); - if (pin_idx < 0) - return; - - hdmi_present_sense(&spec->pins[pin_idx], 1); - snd_hda_jack_report_sync(codec); -} - -static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res) -{ - int tag = res >> AC_UNSOL_RES_TAG_SHIFT; - int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT; - int cp_state = !!(res & AC_UNSOL_RES_CP_STATE); - int cp_ready = !!(res & AC_UNSOL_RES_CP_READY); - - printk(KERN_INFO - "HDMI CP event: CODEC=%d PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n", - codec->addr, - tag, - subtag, - cp_state, - cp_ready); - - /* TODO */ - if (cp_state) - ; - if (cp_ready) - ; -} - - -static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res) -{ - int tag = res >> AC_UNSOL_RES_TAG_SHIFT; - int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT; - - if (!snd_hda_jack_tbl_get_from_tag(codec, 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 - */ - -/* HBR should be Non-PCM, 8 channels */ -#define is_hbr_format(format) \ - ((format & AC_FMT_TYPE_NON_PCM) && (format & AC_FMT_CHAN_MASK) == 7) - -static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, - hda_nid_t pin_nid, u32 stream_tag, int format) -{ - int pinctl; - int new_pinctl = 0; - - if (snd_hda_query_pin_caps(codec, pin_nid) & AC_PINCAP_HBR) { - pinctl = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - - new_pinctl = pinctl & ~AC_PINCTL_EPT; - if (is_hbr_format(format)) - new_pinctl |= AC_PINCTL_EPT_HBR; - else - new_pinctl |= AC_PINCTL_EPT_NATIVE; - - snd_printdd("hdmi_setup_stream: " - "NID=0x%x, %spinctl=0x%x\n", - pin_nid, - pinctl == new_pinctl ? "" : "new-", - new_pinctl); - - if (pinctl != new_pinctl) - snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - new_pinctl); - - } - if (is_hbr_format(format) && !new_pinctl) { - snd_printdd("hdmi_setup_stream: HBR is not supported\n"); - return -EINVAL; - } - - snd_hda_codec_setup_stream(codec, cvt_nid, stream_tag, 0, format); - return 0; -} - -/* - * HDA PCM callbacks - */ -static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hdmi_spec *spec = codec->spec; - struct snd_pcm_runtime *runtime = substream->runtime; - int pin_idx, cvt_idx, mux_idx = 0; - struct hdmi_spec_per_pin *per_pin; - struct hdmi_eld *eld; - struct hdmi_spec_per_cvt *per_cvt = NULL; - int pinctl; - - /* Validate hinfo */ - pin_idx = hinfo_to_pin_index(spec, hinfo); - if (snd_BUG_ON(pin_idx < 0)) - return -EINVAL; - per_pin = &spec->pins[pin_idx]; - eld = &per_pin->sink_eld; - - /* Dynamically assign converter to stream */ - for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) { - per_cvt = &spec->cvts[cvt_idx]; - - /* Must not already be assigned */ - if (per_cvt->assigned) - continue; - /* Must be in pin's mux's list of converters */ - for (mux_idx = 0; mux_idx < per_pin->num_mux_nids; mux_idx++) - if (per_pin->mux_nids[mux_idx] == per_cvt->cvt_nid) - break; - /* Not in mux list */ - if (mux_idx == per_pin->num_mux_nids) - continue; - break; - } - /* No free converters */ - if (cvt_idx == spec->num_cvts) - return -ENODEV; - - /* Claim converter */ - per_cvt->assigned = 1; - hinfo->nid = per_cvt->cvt_nid; - - snd_hda_codec_write(codec, per_pin->pin_nid, 0, - AC_VERB_SET_CONNECT_SEL, - mux_idx); - pinctl = snd_hda_codec_read(codec, per_pin->pin_nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - snd_hda_codec_write(codec, per_pin->pin_nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - pinctl | PIN_OUT); - snd_hda_spdif_ctls_assign(codec, pin_idx, per_cvt->cvt_nid); - - /* Initially set the converter's capabilities */ - hinfo->channels_min = per_cvt->channels_min; - hinfo->channels_max = per_cvt->channels_max; - hinfo->rates = per_cvt->rates; - hinfo->formats = per_cvt->formats; - hinfo->maxbps = per_cvt->maxbps; - - /* Restrict capabilities by ELD if this isn't disabled */ - if (!static_hdmi_pcm && eld->eld_valid) { - snd_hdmi_eld_update_pcm_info(eld, hinfo); - if (hinfo->channels_min > hinfo->channels_max || - !hinfo->rates || !hinfo->formats) - return -ENODEV; - } - - /* Store the updated parameters */ - runtime->hw.channels_min = hinfo->channels_min; - runtime->hw.channels_max = hinfo->channels_max; - runtime->hw.formats = hinfo->formats; - runtime->hw.rates = hinfo->rates; - - snd_pcm_hw_constraint_step(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_CHANNELS, 2); - return 0; -} - -/* - * HDA/HDMI auto parsing - */ -static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx) -{ - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; - hda_nid_t pin_nid = per_pin->pin_nid; - - if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) { - snd_printk(KERN_WARNING - "HDMI: pin %d wcaps %#x " - "does not support connection list\n", - pin_nid, get_wcaps(codec, pin_nid)); - return -EINVAL; - } - - per_pin->num_mux_nids = snd_hda_get_connections(codec, pin_nid, - per_pin->mux_nids, - HDA_MAX_CONNECTIONS); - - return 0; -} - -static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) -{ - struct hda_codec *codec = per_pin->codec; - struct hdmi_eld *eld = &per_pin->sink_eld; - hda_nid_t pin_nid = per_pin->pin_nid; - /* - * Always execute a GetPinSense verb here, even when called from - * hdmi_intrinsic_event; for some NVIDIA HW, the unsolicited - * response's PD bit is not the real PD value, but indicates that - * the real PD value changed. An older version of the HD-audio - * specification worked this way. Hence, we just ignore the data in - * the unsolicited response to avoid custom WARs. - */ - int present = snd_hda_pin_sense(codec, pin_nid); - bool eld_valid = false; - - memset(eld, 0, offsetof(struct hdmi_eld, eld_buffer)); - - eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE); - if (eld->monitor_present) - eld_valid = !!(present & AC_PINSENSE_ELDV); - - _snd_printd(SND_PR_VERBOSE, - "HDMI status: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n", - codec->addr, pin_nid, eld->monitor_present, eld_valid); - - if (eld_valid) { - if (!snd_hdmi_get_eld(eld, codec, pin_nid)) - snd_hdmi_show_eld(eld); - else if (repoll) { - queue_delayed_work(codec->bus->workq, - &per_pin->work, - msecs_to_jiffies(300)); - } - } -} - -static void hdmi_repoll_eld(struct work_struct *work) -{ - struct hdmi_spec_per_pin *per_pin = - container_of(to_delayed_work(work), struct hdmi_spec_per_pin, work); - - if (per_pin->repoll_count++ > 6) - per_pin->repoll_count = 0; - - hdmi_present_sense(per_pin, per_pin->repoll_count); -} - -static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) -{ - struct hdmi_spec *spec = codec->spec; - unsigned int caps, config; - int pin_idx; - struct hdmi_spec_per_pin *per_pin; - int err; - - caps = snd_hda_param_read(codec, pin_nid, AC_PAR_PIN_CAP); - if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP))) - return 0; - - config = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_CONFIG_DEFAULT, 0); - if (get_defcfg_connect(config) == AC_JACK_PORT_NONE) - return 0; - - if (snd_BUG_ON(spec->num_pins >= MAX_HDMI_PINS)) - return -E2BIG; - - pin_idx = spec->num_pins; - per_pin = &spec->pins[pin_idx]; - - per_pin->pin_nid = pin_nid; - - err = hdmi_read_pin_conn(codec, pin_idx); - if (err < 0) - return err; - - spec->num_pins++; - - return 0; -} - -static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid) -{ - struct hdmi_spec *spec = codec->spec; - int cvt_idx; - struct hdmi_spec_per_cvt *per_cvt; - unsigned int chans; - int err; - - if (snd_BUG_ON(spec->num_cvts >= MAX_HDMI_CVTS)) - return -E2BIG; - - chans = get_wcaps(codec, cvt_nid); - chans = get_wcaps_channels(chans); - - cvt_idx = spec->num_cvts; - per_cvt = &spec->cvts[cvt_idx]; - - per_cvt->cvt_nid = cvt_nid; - per_cvt->channels_min = 2; - if (chans <= 16) - per_cvt->channels_max = chans; - - err = snd_hda_query_supported_pcm(codec, cvt_nid, - &per_cvt->rates, - &per_cvt->formats, - &per_cvt->maxbps); - if (err < 0) - return err; - - spec->num_cvts++; - - return 0; -} - -static int hdmi_parse_codec(struct hda_codec *codec) -{ - hda_nid_t nid; - int i, nodes; - - nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid); - if (!nid || nodes < 0) { - snd_printk(KERN_WARNING "HDMI: failed to get afg sub nodes\n"); - return -EINVAL; - } - - for (i = 0; i < nodes; i++, nid++) { - unsigned int caps; - unsigned int type; - - caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP); - type = get_wcaps_type(caps); - - if (!(caps & AC_WCAP_DIGITAL)) - continue; - - switch (type) { - case AC_WID_AUD_OUT: - hdmi_add_cvt(codec, nid); - break; - case AC_WID_PIN: - hdmi_add_pin(codec, nid); - break; - } - } - - /* - * G45/IbexPeak don't support EPSS: the unsolicited pin hot plug event - * can be lost and presence sense verb will become inaccurate if the - * HDA link is powered off at hot plug or hw initialization time. - */ -#ifdef CONFIG_SND_HDA_POWER_SAVE - if (!(snd_hda_param_read(codec, codec->afg, AC_PAR_POWER_STATE) & - AC_PWRST_EPSS)) - codec->bus->power_keep_link_on = 1; -#endif - - return 0; -} - -/* - */ -static char *get_hdmi_pcm_name(int idx) -{ - static char names[MAX_HDMI_PINS][8]; - sprintf(&names[idx][0], "HDMI %d", idx); - return &names[idx][0]; -} - -/* - * HDMI callbacks - */ - -static int generic_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) -{ - hda_nid_t cvt_nid = hinfo->nid; - struct hdmi_spec *spec = codec->spec; - int pin_idx = hinfo_to_pin_index(spec, hinfo); - hda_nid_t pin_nid = spec->pins[pin_idx].pin_nid; - - hdmi_set_channel_count(codec, cvt_nid, substream->runtime->channels); - - hdmi_setup_audio_infoframe(codec, pin_idx, substream); - - return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format); -} - -static int generic_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hdmi_spec *spec = codec->spec; - int cvt_idx, pin_idx; - struct hdmi_spec_per_cvt *per_cvt; - struct hdmi_spec_per_pin *per_pin; - int pinctl; - - snd_hda_codec_cleanup_stream(codec, hinfo->nid); - - if (hinfo->nid) { - cvt_idx = cvt_nid_to_cvt_index(spec, hinfo->nid); - if (snd_BUG_ON(cvt_idx < 0)) - return -EINVAL; - per_cvt = &spec->cvts[cvt_idx]; - - snd_BUG_ON(!per_cvt->assigned); - per_cvt->assigned = 0; - hinfo->nid = 0; - - pin_idx = hinfo_to_pin_index(spec, hinfo); - if (snd_BUG_ON(pin_idx < 0)) - return -EINVAL; - per_pin = &spec->pins[pin_idx]; - - pinctl = snd_hda_codec_read(codec, per_pin->pin_nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - snd_hda_codec_write(codec, per_pin->pin_nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - pinctl & ~PIN_OUT); - snd_hda_spdif_ctls_unassign(codec, pin_idx); - } - - return 0; -} - -static const struct hda_pcm_ops generic_ops = { - .open = hdmi_pcm_open, - .prepare = generic_hdmi_playback_pcm_prepare, - .cleanup = generic_hdmi_playback_pcm_cleanup, -}; - -static int generic_hdmi_build_pcms(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int pin_idx; - - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - struct hda_pcm *info; - struct hda_pcm_stream *pstr; - - info = &spec->pcm_rec[pin_idx]; - info->name = get_hdmi_pcm_name(pin_idx); - info->pcm_type = HDA_PCM_TYPE_HDMI; - - pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK]; - pstr->substreams = 1; - pstr->ops = generic_ops; - /* other pstr fields are set in open */ - } - - codec->num_pcms = spec->num_pins; - codec->pcm_info = spec->pcm_rec; - - return 0; -} - -static int generic_hdmi_build_jack(struct hda_codec *codec, int pin_idx) -{ - char hdmi_str[32] = "HDMI/DP"; - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; - int pcmdev = spec->pcm_rec[pin_idx].device; - - if (pcmdev > 0) - sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev); - - return snd_hda_jack_add_kctl(codec, per_pin->pin_nid, hdmi_str, 0); -} - -static int generic_hdmi_build_controls(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int err; - int pin_idx; - - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; - - err = generic_hdmi_build_jack(codec, pin_idx); - if (err < 0) - return err; - - err = snd_hda_create_spdif_out_ctls(codec, - per_pin->pin_nid, - per_pin->mux_nids[0]); - if (err < 0) - return err; - snd_hda_spdif_ctls_unassign(codec, pin_idx); - - /* add control for ELD Bytes */ - err = hdmi_create_eld_ctl(codec, - pin_idx, - spec->pcm_rec[pin_idx].device); - - if (err < 0) - return err; - - hdmi_present_sense(per_pin, 0); - } - - return 0; -} - -static int generic_hdmi_init(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int pin_idx; - - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; - hda_nid_t pin_nid = per_pin->pin_nid; - struct hdmi_eld *eld = &per_pin->sink_eld; - - hdmi_init_pin(codec, pin_nid); - snd_hda_jack_detect_enable(codec, pin_nid, pin_nid); - - per_pin->codec = codec; - INIT_DELAYED_WORK(&per_pin->work, hdmi_repoll_eld); - snd_hda_eld_proc_new(codec, eld, pin_idx); - } - snd_hda_jack_report_sync(codec); - return 0; -} - -static void generic_hdmi_free(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int pin_idx; - - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; - struct hdmi_eld *eld = &per_pin->sink_eld; - - cancel_delayed_work(&per_pin->work); - snd_hda_eld_proc_free(codec, eld); - } - - flush_workqueue(codec->bus->workq); - kfree(spec); -} - -static const struct hda_codec_ops generic_hdmi_patch_ops = { - .init = generic_hdmi_init, - .free = generic_hdmi_free, - .build_pcms = generic_hdmi_build_pcms, - .build_controls = generic_hdmi_build_controls, - .unsol_event = hdmi_unsol_event, -}; - -static int patch_generic_hdmi(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - if (hdmi_parse_codec(codec) < 0) { - codec->spec = NULL; - kfree(spec); - return -EINVAL; - } - codec->patch_ops = generic_hdmi_patch_ops; - - init_channel_allocations(); - - return 0; -} - -/* - * Shared non-generic implementations - */ - -static int simple_playback_build_pcms(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; - int i; - - codec->num_pcms = spec->num_cvts; - codec->pcm_info = info; - - for (i = 0; i < codec->num_pcms; i++, info++) { - unsigned int chans; - struct hda_pcm_stream *pstr; - - chans = get_wcaps(codec, spec->cvts[i].cvt_nid); - chans = get_wcaps_channels(chans); - - info->name = get_hdmi_pcm_name(i); - info->pcm_type = HDA_PCM_TYPE_HDMI; - pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK]; - snd_BUG_ON(!spec->pcm_playback); - *pstr = *spec->pcm_playback; - pstr->nid = spec->cvts[i].cvt_nid; - if (pstr->channels_max <= 2 && chans && chans <= 16) - pstr->channels_max = chans; - } - - return 0; -} - -static int simple_playback_build_controls(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int err; - int i; - - for (i = 0; i < codec->num_pcms; i++) { - err = snd_hda_create_spdif_out_ctls(codec, - spec->cvts[i].cvt_nid, - spec->cvts[i].cvt_nid); - if (err < 0) - return err; - } - - return 0; -} - -static void simple_playback_free(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - - kfree(spec); -} - -/* - * Nvidia specific implementations - */ - -#define Nv_VERB_SET_Channel_Allocation 0xF79 -#define Nv_VERB_SET_Info_Frame_Checksum 0xF7A -#define Nv_VERB_SET_Audio_Protection_On 0xF98 -#define Nv_VERB_SET_Audio_Protection_Off 0xF99 - -#define nvhdmi_master_con_nid_7x 0x04 -#define nvhdmi_master_pin_nid_7x 0x05 - -static const hda_nid_t nvhdmi_con_nids_7x[4] = { - /*front, rear, clfe, rear_surr */ - 0x6, 0x8, 0xa, 0xc, -}; - -static const struct hda_verb nvhdmi_basic_init_7x[] = { - /* set audio protect on */ - { 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1}, - /* enable digital output on pin widget */ - { 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, - { 0x7, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, - { 0x9, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, - { 0xb, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, - { 0xd, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, - {} /* terminator */ -}; - -#ifdef LIMITED_RATE_FMT_SUPPORT -/* support only the safe format and rate */ -#define SUPPORTED_RATES SNDRV_PCM_RATE_48000 -#define SUPPORTED_MAXBPS 16 -#define SUPPORTED_FORMATS SNDRV_PCM_FMTBIT_S16_LE -#else -/* support all rates and formats */ -#define SUPPORTED_RATES \ - (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ - SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\ - SNDRV_PCM_RATE_192000) -#define SUPPORTED_MAXBPS 24 -#define SUPPORTED_FORMATS \ - (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) -#endif - -static int nvhdmi_7x_init(struct hda_codec *codec) -{ - snd_hda_sequence_write(codec, nvhdmi_basic_init_7x); - return 0; -} - -static unsigned int channels_2_6_8[] = { - 2, 6, 8 -}; - -static unsigned int channels_2_8[] = { - 2, 8 -}; - -static struct snd_pcm_hw_constraint_list hw_constraints_2_6_8_channels = { - .count = ARRAY_SIZE(channels_2_6_8), - .list = channels_2_6_8, - .mask = 0, -}; - -static struct snd_pcm_hw_constraint_list hw_constraints_2_8_channels = { - .count = ARRAY_SIZE(channels_2_8), - .list = channels_2_8, - .mask = 0, -}; - -static int simple_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hdmi_spec *spec = codec->spec; - struct snd_pcm_hw_constraint_list *hw_constraints_channels = NULL; - - switch (codec->preset->id) { - case 0x10de0002: - case 0x10de0003: - case 0x10de0005: - case 0x10de0006: - hw_constraints_channels = &hw_constraints_2_8_channels; - break; - case 0x10de0007: - hw_constraints_channels = &hw_constraints_2_6_8_channels; - break; - default: - break; - } - - if (hw_constraints_channels != NULL) { - snd_pcm_hw_constraint_list(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_CHANNELS, - hw_constraints_channels); - } else { - snd_pcm_hw_constraint_step(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_CHANNELS, 2); - } - - return snd_hda_multi_out_dig_open(codec, &spec->multiout); -} - -static int simple_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hdmi_spec *spec = codec->spec; - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} - -static int simple_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 hdmi_spec *spec = codec->spec; - return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, - stream_tag, format, substream); -} - -static void nvhdmi_8ch_7x_set_info_frame_parameters(struct hda_codec *codec, - int channels) -{ - unsigned int chanmask; - int chan = channels ? (channels - 1) : 1; - - switch (channels) { - default: - case 0: - case 2: - chanmask = 0x00; - break; - case 4: - chanmask = 0x08; - break; - case 6: - chanmask = 0x0b; - break; - case 8: - chanmask = 0x13; - break; - } - - /* Set the audio infoframe channel allocation and checksum fields. The - * channel count is computed implicitly by the hardware. */ - snd_hda_codec_write(codec, 0x1, 0, - Nv_VERB_SET_Channel_Allocation, chanmask); - - snd_hda_codec_write(codec, 0x1, 0, - Nv_VERB_SET_Info_Frame_Checksum, - (0x71 - chan - chanmask)); -} - -static int nvhdmi_8ch_7x_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hdmi_spec *spec = codec->spec; - int i; - - snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, - 0, AC_VERB_SET_CHANNEL_STREAMID, 0); - for (i = 0; i < 4; i++) { - /* set the stream id */ - snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0, - AC_VERB_SET_CHANNEL_STREAMID, 0); - /* set the stream format */ - snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0, - AC_VERB_SET_STREAM_FORMAT, 0); - } - - /* The audio hardware sends a channel count of 0x7 (8ch) when all the - * streams are disabled. */ - nvhdmi_8ch_7x_set_info_frame_parameters(codec, 8); - - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} - -static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - int chs; - unsigned int dataDCC2, channel_id; - int i; - struct hdmi_spec *spec = codec->spec; - struct hda_spdif_out *spdif = - snd_hda_spdif_out_of_nid(codec, spec->cvts[0].cvt_nid); - - mutex_lock(&codec->spdif_mutex); - - chs = substream->runtime->channels; - - dataDCC2 = 0x2; - - /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */ - if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE)) - snd_hda_codec_write(codec, - nvhdmi_master_con_nid_7x, - 0, - AC_VERB_SET_DIGI_CONVERT_1, - spdif->ctls & ~AC_DIG1_ENABLE & 0xff); - - /* set the stream id */ - snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0, - AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0); - - /* set the stream format */ - snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0, - AC_VERB_SET_STREAM_FORMAT, format); - - /* turn on again (if needed) */ - /* enable and set the channel status audio/data flag */ - if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE)) { - snd_hda_codec_write(codec, - nvhdmi_master_con_nid_7x, - 0, - AC_VERB_SET_DIGI_CONVERT_1, - spdif->ctls & 0xff); - snd_hda_codec_write(codec, - nvhdmi_master_con_nid_7x, - 0, - AC_VERB_SET_DIGI_CONVERT_2, dataDCC2); - } - - for (i = 0; i < 4; i++) { - if (chs == 2) - channel_id = 0; - else - channel_id = i * 2; - - /* turn off SPDIF once; - *otherwise the IEC958 bits won't be updated - */ - if (codec->spdif_status_reset && - (spdif->ctls & AC_DIG1_ENABLE)) - snd_hda_codec_write(codec, - nvhdmi_con_nids_7x[i], - 0, - AC_VERB_SET_DIGI_CONVERT_1, - spdif->ctls & ~AC_DIG1_ENABLE & 0xff); - /* set the stream id */ - snd_hda_codec_write(codec, - nvhdmi_con_nids_7x[i], - 0, - AC_VERB_SET_CHANNEL_STREAMID, - (stream_tag << 4) | channel_id); - /* set the stream format */ - snd_hda_codec_write(codec, - nvhdmi_con_nids_7x[i], - 0, - AC_VERB_SET_STREAM_FORMAT, - format); - /* turn on again (if needed) */ - /* enable and set the channel status audio/data flag */ - if (codec->spdif_status_reset && - (spdif->ctls & AC_DIG1_ENABLE)) { - snd_hda_codec_write(codec, - nvhdmi_con_nids_7x[i], - 0, - AC_VERB_SET_DIGI_CONVERT_1, - spdif->ctls & 0xff); - snd_hda_codec_write(codec, - nvhdmi_con_nids_7x[i], - 0, - AC_VERB_SET_DIGI_CONVERT_2, dataDCC2); - } - } - - nvhdmi_8ch_7x_set_info_frame_parameters(codec, chs); - - mutex_unlock(&codec->spdif_mutex); - return 0; -} - -static const struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = { - .substreams = 1, - .channels_min = 2, - .channels_max = 8, - .nid = nvhdmi_master_con_nid_7x, - .rates = SUPPORTED_RATES, - .maxbps = SUPPORTED_MAXBPS, - .formats = SUPPORTED_FORMATS, - .ops = { - .open = simple_playback_pcm_open, - .close = nvhdmi_8ch_7x_pcm_close, - .prepare = nvhdmi_8ch_7x_pcm_prepare - }, -}; - -static const struct hda_pcm_stream nvhdmi_pcm_playback_2ch = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .nid = nvhdmi_master_con_nid_7x, - .rates = SUPPORTED_RATES, - .maxbps = SUPPORTED_MAXBPS, - .formats = SUPPORTED_FORMATS, - .ops = { - .open = simple_playback_pcm_open, - .close = simple_playback_pcm_close, - .prepare = simple_playback_pcm_prepare - }, -}; - -static const struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = { - .build_controls = simple_playback_build_controls, - .build_pcms = simple_playback_build_pcms, - .init = nvhdmi_7x_init, - .free = simple_playback_free, -}; - -static const struct hda_codec_ops nvhdmi_patch_ops_2ch = { - .build_controls = simple_playback_build_controls, - .build_pcms = simple_playback_build_pcms, - .init = nvhdmi_7x_init, - .free = simple_playback_free, -}; - -static int patch_nvhdmi_2ch(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - - spec->multiout.num_dacs = 0; /* no analog */ - spec->multiout.max_channels = 2; - spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x; - spec->num_cvts = 1; - spec->cvts[0].cvt_nid = nvhdmi_master_con_nid_7x; - spec->pcm_playback = &nvhdmi_pcm_playback_2ch; - - codec->patch_ops = nvhdmi_patch_ops_2ch; - - return 0; -} - -static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - int err = patch_nvhdmi_2ch(codec); - - if (err < 0) - return err; - spec = codec->spec; - spec->multiout.max_channels = 8; - spec->pcm_playback = &nvhdmi_pcm_playback_8ch_7x; - codec->patch_ops = nvhdmi_patch_ops_8ch_7x; - - /* Initialize the audio infoframe channel mask and checksum to something - * valid */ - nvhdmi_8ch_7x_set_info_frame_parameters(codec, 8); - - return 0; -} - -/* - * ATI-specific implementations - * - * FIXME: we may omit the whole this and use the generic code once after - * it's confirmed to work. - */ - -#define ATIHDMI_CVT_NID 0x02 /* audio converter */ -#define ATIHDMI_PIN_NID 0x03 /* HDMI output pin */ - -static int atihdmi_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 hdmi_spec *spec = codec->spec; - int chans = substream->runtime->channels; - int i, err; - - err = simple_playback_pcm_prepare(hinfo, codec, stream_tag, format, - substream); - if (err < 0) - return err; - snd_hda_codec_write(codec, spec->cvts[0].cvt_nid, 0, - AC_VERB_SET_CVT_CHAN_COUNT, chans - 1); - /* FIXME: XXX */ - for (i = 0; i < chans; i++) { - snd_hda_codec_write(codec, spec->cvts[0].cvt_nid, 0, - AC_VERB_SET_HDMI_CHAN_SLOT, - (i << 4) | i); - } - return 0; -} - -static const struct hda_pcm_stream atihdmi_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .nid = ATIHDMI_CVT_NID, - .ops = { - .open = simple_playback_pcm_open, - .close = simple_playback_pcm_close, - .prepare = atihdmi_playback_pcm_prepare - }, -}; - -static const struct hda_verb atihdmi_basic_init[] = { - /* enable digital output on pin widget */ - { 0x03, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - {} /* terminator */ -}; - -static int atihdmi_init(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - - snd_hda_sequence_write(codec, atihdmi_basic_init); - /* SI codec requires to unmute the pin */ - if (get_wcaps(codec, spec->pins[0].pin_nid) & AC_WCAP_OUT_AMP) - snd_hda_codec_write(codec, spec->pins[0].pin_nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); - return 0; -} - -static const struct hda_codec_ops atihdmi_patch_ops = { - .build_controls = simple_playback_build_controls, - .build_pcms = simple_playback_build_pcms, - .init = atihdmi_init, - .free = simple_playback_free, -}; - - -static int patch_atihdmi(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - - spec->multiout.num_dacs = 0; /* no analog */ - spec->multiout.max_channels = 2; - spec->multiout.dig_out_nid = ATIHDMI_CVT_NID; - spec->num_cvts = 1; - spec->cvts[0].cvt_nid = ATIHDMI_CVT_NID; - spec->pins[0].pin_nid = ATIHDMI_PIN_NID; - spec->pcm_playback = &atihdmi_pcm_digital_playback; - - codec->patch_ops = atihdmi_patch_ops; - - return 0; -} - - -/* - * patch entries - */ -static const struct hda_codec_preset snd_hda_preset_hdmi[] = { -{ .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_generic_hdmi }, -{ .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_generic_hdmi }, -{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_generic_hdmi }, -{ .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_generic_hdmi }, -{ .id = 0x10de0002, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x }, -{ .id = 0x10de0003, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x }, -{ .id = 0x10de0005, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x }, -{ .id = 0x10de0006, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x }, -{ .id = 0x10de0007, .name = "MCP79/7A HDMI", .patch = patch_nvhdmi_8ch_7x }, -{ .id = 0x10de000a, .name = "GPU 0a HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de000b, .name = "GPU 0b HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de000c, .name = "MCP89 HDMI", .patch = patch_generic_hdmi }, -{ .id = 0x10de000d, .name = "GPU 0d HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de0010, .name = "GPU 10 HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de0011, .name = "GPU 11 HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de0012, .name = "GPU 12 HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de0013, .name = "GPU 13 HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de0014, .name = "GPU 14 HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de0015, .name = "GPU 15 HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de0016, .name = "GPU 16 HDMI/DP", .patch = patch_generic_hdmi }, -/* 17 is known to be absent */ -{ .id = 0x10de0018, .name = "GPU 18 HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de0019, .name = "GPU 19 HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch }, -{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch }, -{ .id = 0x80860054, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi }, -{ .id = 0x80862801, .name = "Bearlake HDMI", .patch = patch_generic_hdmi }, -{ .id = 0x80862802, .name = "Cantiga HDMI", .patch = patch_generic_hdmi }, -{ .id = 0x80862803, .name = "Eaglelake HDMI", .patch = patch_generic_hdmi }, -{ .id = 0x80862804, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi }, -{ .id = 0x80862805, .name = "CougarPoint HDMI", .patch = patch_generic_hdmi }, -{ .id = 0x80862806, .name = "PantherPoint HDMI", .patch = patch_generic_hdmi }, -{ .id = 0x80862880, .name = "CedarTrail HDMI", .patch = patch_generic_hdmi }, -{ .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_generic_hdmi }, -{} /* 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:10951392"); -MODULE_ALIAS("snd-hda-codec-id:10de0002"); -MODULE_ALIAS("snd-hda-codec-id:10de0003"); -MODULE_ALIAS("snd-hda-codec-id:10de0005"); -MODULE_ALIAS("snd-hda-codec-id:10de0006"); -MODULE_ALIAS("snd-hda-codec-id:10de0007"); -MODULE_ALIAS("snd-hda-codec-id:10de000a"); -MODULE_ALIAS("snd-hda-codec-id:10de000b"); -MODULE_ALIAS("snd-hda-codec-id:10de000c"); -MODULE_ALIAS("snd-hda-codec-id:10de000d"); -MODULE_ALIAS("snd-hda-codec-id:10de0010"); -MODULE_ALIAS("snd-hda-codec-id:10de0011"); -MODULE_ALIAS("snd-hda-codec-id:10de0012"); -MODULE_ALIAS("snd-hda-codec-id:10de0013"); -MODULE_ALIAS("snd-hda-codec-id:10de0014"); -MODULE_ALIAS("snd-hda-codec-id:10de0015"); -MODULE_ALIAS("snd-hda-codec-id:10de0016"); -MODULE_ALIAS("snd-hda-codec-id:10de0018"); -MODULE_ALIAS("snd-hda-codec-id:10de0019"); -MODULE_ALIAS("snd-hda-codec-id:10de001a"); -MODULE_ALIAS("snd-hda-codec-id:10de001b"); -MODULE_ALIAS("snd-hda-codec-id:10de001c"); -MODULE_ALIAS("snd-hda-codec-id:10de0040"); -MODULE_ALIAS("snd-hda-codec-id:10de0041"); -MODULE_ALIAS("snd-hda-codec-id:10de0042"); -MODULE_ALIAS("snd-hda-codec-id:10de0043"); -MODULE_ALIAS("snd-hda-codec-id:10de0044"); -MODULE_ALIAS("snd-hda-codec-id:10de0067"); -MODULE_ALIAS("snd-hda-codec-id:10de8001"); -MODULE_ALIAS("snd-hda-codec-id:17e80047"); -MODULE_ALIAS("snd-hda-codec-id:80860054"); -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:80862804"); -MODULE_ALIAS("snd-hda-codec-id:80862805"); -MODULE_ALIAS("snd-hda-codec-id:80862806"); -MODULE_ALIAS("snd-hda-codec-id:80862880"); -MODULE_ALIAS("snd-hda-codec-id:808629fb"); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("HDMI HD-audio codec"); -MODULE_ALIAS("snd-hda-codec-intelhdmi"); -MODULE_ALIAS("snd-hda-codec-nvhdmi"); -MODULE_ALIAS("snd-hda-codec-atihdmi"); - -static struct hda_codec_preset_list intel_list = { - .preset = snd_hda_preset_hdmi, - .owner = THIS_MODULE, -}; - -static int __init patch_hdmi_init(void) -{ - return snd_hda_add_codec_preset(&intel_list); -} - -static void __exit patch_hdmi_exit(void) -{ - snd_hda_delete_codec_preset(&intel_list); -} - -module_init(patch_hdmi_init) -module_exit(patch_hdmi_exit) diff --git a/ANDROID_3.4.5/sound/pci/hda/patch_realtek.c b/ANDROID_3.4.5/sound/pci/hda/patch_realtek.c deleted file mode 100644 index c43264f5..00000000 --- a/ANDROID_3.4.5/sound/pci/hda/patch_realtek.c +++ /dev/null @@ -1,7034 +0,0 @@ -/* - * Universal Interface for Intel High Definition Audio Codec - * - * HD audio interface patch for Realtek ALC codecs - * - * Copyright (c) 2004 Kailang Yang <kailang@realtek.com.tw> - * PeiSen Hou <pshou@realtek.com.tw> - * Takashi Iwai <tiwai@suse.de> - * Jonathan Woithe <jwoithe@physics.adelaide.edu.au> - * - * 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 <linux/delay.h> -#include <linux/slab.h> -#include <linux/pci.h> -#include <linux/module.h> -#include <sound/core.h> -#include <sound/jack.h> -#include "hda_codec.h" -#include "hda_local.h" -#include "hda_beep.h" -#include "hda_jack.h" - -/* unsol event tags */ -#define ALC_FRONT_EVENT 0x01 -#define ALC_DCVOL_EVENT 0x02 -#define ALC_HP_EVENT 0x04 -#define ALC_MIC_EVENT 0x08 - -/* for GPIO Poll */ -#define GPIO_MASK 0x03 - -/* extra amp-initialization sequence types */ -enum { - ALC_INIT_NONE, - ALC_INIT_DEFAULT, - ALC_INIT_GPIO1, - ALC_INIT_GPIO2, - ALC_INIT_GPIO3, -}; - -struct alc_customize_define { - unsigned int sku_cfg; - unsigned char port_connectivity; - unsigned char check_sum; - unsigned char customization; - unsigned char external_amp; - unsigned int enable_pcbeep:1; - unsigned int platform_type:1; - unsigned int swap:1; - unsigned int override:1; - unsigned int fixup:1; /* Means that this sku is set by driver, not read from hw */ -}; - -struct alc_fixup; - -struct alc_multi_io { - hda_nid_t pin; /* multi-io widget pin NID */ - hda_nid_t dac; /* DAC to be connected */ - unsigned int ctl_in; /* cached input-pin control value */ -}; - -enum { - ALC_AUTOMUTE_PIN, /* change the pin control */ - ALC_AUTOMUTE_AMP, /* mute/unmute the pin AMP */ - ALC_AUTOMUTE_MIXER, /* mute/unmute mixer widget AMP */ -}; - -#define MAX_VOL_NIDS 0x40 - -struct alc_spec { - /* codec parameterization */ - const struct snd_kcontrol_new *mixers[5]; /* mixer arrays */ - unsigned int num_mixers; - const struct snd_kcontrol_new *cap_mixer; /* capture mixer */ - unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */ - - const struct hda_verb *init_verbs[10]; /* initialization verbs - * don't forget NULL - * termination! - */ - unsigned int num_init_verbs; - - char stream_name_analog[32]; /* analog PCM stream */ - const struct hda_pcm_stream *stream_analog_playback; - const struct hda_pcm_stream *stream_analog_capture; - const struct hda_pcm_stream *stream_analog_alt_playback; - const struct hda_pcm_stream *stream_analog_alt_capture; - - char stream_name_digital[32]; /* digital PCM stream */ - const struct hda_pcm_stream *stream_digital_playback; - const struct hda_pcm_stream *stream_digital_capture; - - /* playback */ - struct hda_multi_out multiout; /* playback set-up - * max_channels, dacs must be set - * dig_out_nid and hp_nid are optional - */ - hda_nid_t alt_dac_nid; - hda_nid_t slave_dig_outs[3]; /* optional - for auto-parsing */ - int dig_out_type; - - /* capture */ - unsigned int num_adc_nids; - const hda_nid_t *adc_nids; - const hda_nid_t *capsrc_nids; - hda_nid_t dig_in_nid; /* digital-in NID; optional */ - hda_nid_t mixer_nid; /* analog-mixer NID */ - DECLARE_BITMAP(vol_ctls, MAX_VOL_NIDS << 1); - DECLARE_BITMAP(sw_ctls, MAX_VOL_NIDS << 1); - - /* capture setup for dynamic dual-adc switch */ - hda_nid_t cur_adc; - unsigned int cur_adc_stream_tag; - unsigned int cur_adc_format; - - /* capture source */ - unsigned int num_mux_defs; - const struct hda_input_mux *input_mux; - unsigned int cur_mux[3]; - hda_nid_t ext_mic_pin; - hda_nid_t dock_mic_pin; - hda_nid_t int_mic_pin; - - /* channel model */ - const struct hda_channel_mode *channel_mode; - int num_channel_mode; - int need_dac_fix; - int const_channel_count; - int ext_channel_count; - - /* PCM information */ - struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */ - - /* dynamic controls, init_verbs and input_mux */ - struct auto_pin_cfg autocfg; - struct alc_customize_define cdefine; - struct snd_array kctls; - struct hda_input_mux private_imux[3]; - hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; - hda_nid_t private_adc_nids[AUTO_CFG_MAX_OUTS]; - hda_nid_t private_capsrc_nids[AUTO_CFG_MAX_OUTS]; - hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS]; - unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS]; - int int_mic_idx, ext_mic_idx, dock_mic_idx; /* for auto-mic */ - - /* hooks */ - void (*init_hook)(struct hda_codec *codec); - void (*unsol_event)(struct hda_codec *codec, unsigned int res); -#ifdef CONFIG_SND_HDA_POWER_SAVE - void (*power_hook)(struct hda_codec *codec); -#endif - void (*shutup)(struct hda_codec *codec); - void (*automute_hook)(struct hda_codec *codec); - - /* for pin sensing */ - unsigned int hp_jack_present:1; - unsigned int line_jack_present:1; - unsigned int master_mute:1; - unsigned int auto_mic:1; - unsigned int auto_mic_valid_imux:1; /* valid imux for auto-mic */ - unsigned int automute_speaker:1; /* automute speaker outputs */ - unsigned int automute_lo:1; /* automute LO outputs */ - unsigned int detect_hp:1; /* Headphone detection enabled */ - unsigned int detect_lo:1; /* Line-out detection enabled */ - unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */ - unsigned int automute_lo_possible:1; /* there are line outs and HP */ - unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */ - - /* other flags */ - unsigned int no_analog :1; /* digital I/O only */ - unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */ - unsigned int single_input_src:1; - unsigned int vol_in_capsrc:1; /* use capsrc volume (ADC has no vol) */ - unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */ - unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */ - - /* auto-mute control */ - int automute_mode; - hda_nid_t automute_mixer_nid[AUTO_CFG_MAX_OUTS]; - - int init_amp; - int codec_variant; /* flag for other variants */ - - /* for virtual master */ - hda_nid_t vmaster_nid; - struct hda_vmaster_mute_hook vmaster_mute; -#ifdef CONFIG_SND_HDA_POWER_SAVE - struct hda_loopback_check loopback; - int num_loopbacks; - struct hda_amp_list loopback_list[8]; -#endif - - /* for PLL fix */ - hda_nid_t pll_nid; - unsigned int pll_coef_idx, pll_coef_bit; - unsigned int coef0; - - /* fix-up list */ - int fixup_id; - const struct alc_fixup *fixup_list; - const char *fixup_name; - - /* multi-io */ - int multi_ios; - struct alc_multi_io multi_io[4]; - - /* bind volumes */ - struct snd_array bind_ctls; -}; - -static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, - int dir, unsigned int bits) -{ - if (!nid) - return false; - if (get_wcaps(codec, nid) & (1 << (dir + 1))) - if (query_amp_caps(codec, nid, dir) & bits) - return true; - return false; -} - -#define nid_has_mute(codec, nid, dir) \ - check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE) -#define nid_has_volume(codec, nid, dir) \ - check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS) - -/* - * input MUX handling - */ -static int alc_mux_enum_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; - unsigned int mux_idx = snd_ctl_get_ioffidx(kcontrol, &uinfo->id); - if (mux_idx >= spec->num_mux_defs) - mux_idx = 0; - if (!spec->input_mux[mux_idx].num_items && mux_idx > 0) - mux_idx = 0; - return snd_hda_input_mux_info(&spec->input_mux[mux_idx], uinfo); -} - -static int alc_mux_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - - ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; - return 0; -} - -static bool alc_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t new_adc = spec->adc_nids[spec->dyn_adc_idx[cur]]; - - if (spec->cur_adc && spec->cur_adc != new_adc) { - /* stream is running, let's swap the current ADC */ - __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); - spec->cur_adc = new_adc; - snd_hda_codec_setup_stream(codec, new_adc, - spec->cur_adc_stream_tag, 0, - spec->cur_adc_format); - return true; - } - return false; -} - -static inline hda_nid_t get_capsrc(struct alc_spec *spec, int idx) -{ - return spec->capsrc_nids ? - spec->capsrc_nids[idx] : spec->adc_nids[idx]; -} - -static void call_update_outputs(struct hda_codec *codec); - -/* select the given imux item; either unmute exclusively or select the route */ -static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx, - unsigned int idx, bool force) -{ - struct alc_spec *spec = codec->spec; - const struct hda_input_mux *imux; - unsigned int mux_idx; - int i, type, num_conns; - hda_nid_t nid; - - if (!spec->input_mux) - return 0; - - mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx; - imux = &spec->input_mux[mux_idx]; - if (!imux->num_items && mux_idx > 0) - imux = &spec->input_mux[0]; - if (!imux->num_items) - return 0; - - if (idx >= imux->num_items) - idx = imux->num_items - 1; - if (spec->cur_mux[adc_idx] == idx && !force) - return 0; - spec->cur_mux[adc_idx] = idx; - - /* for shared I/O, change the pin-control accordingly */ - if (spec->shared_mic_hp) { - /* NOTE: this assumes that there are only two inputs, the - * first is the real internal mic and the second is HP jack. - */ - snd_hda_codec_write(codec, spec->autocfg.inputs[1].pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - spec->cur_mux[adc_idx] ? - PIN_VREF80 : PIN_HP); - spec->automute_speaker = !spec->cur_mux[adc_idx]; - call_update_outputs(codec); - } - - if (spec->dyn_adc_switch) { - alc_dyn_adc_pcm_resetup(codec, idx); - adc_idx = spec->dyn_adc_idx[idx]; - } - - nid = get_capsrc(spec, adc_idx); - - /* no selection? */ - num_conns = snd_hda_get_conn_list(codec, nid, NULL); - if (num_conns <= 1) - return 1; - - type = get_wcaps_type(get_wcaps(codec, nid)); - if (type == AC_WID_AUD_MIX) { - /* Matrix-mixer style (e.g. ALC882) */ - int active = imux->items[idx].index; - for (i = 0; i < num_conns; i++) { - unsigned int v = (i == active) ? 0 : HDA_AMP_MUTE; - snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, i, - HDA_AMP_MUTE, v); - } - } else { - /* MUX style (e.g. ALC880) */ - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, - imux->items[idx].index); - } - return 1; -} - -static int alc_mux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - return alc_mux_select(codec, adc_idx, - ucontrol->value.enumerated.item[0], false); -} - -/* - * set up the input pin config (depending on the given auto-pin type) - */ -static void alc_set_input_pin(struct hda_codec *codec, hda_nid_t nid, - int auto_pin_type) -{ - unsigned int val = PIN_IN; - - if (auto_pin_type == AUTO_PIN_MIC) { - unsigned int pincap; - unsigned int oldval; - oldval = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - pincap = snd_hda_query_pin_caps(codec, nid); - pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; - /* if the default pin setup is vref50, we give it priority */ - if ((pincap & AC_PINCAP_VREF_80) && oldval != PIN_VREF50) - val = PIN_VREF80; - else if (pincap & AC_PINCAP_VREF_50) - val = PIN_VREF50; - else if (pincap & AC_PINCAP_VREF_100) - val = PIN_VREF100; - else if (pincap & AC_PINCAP_VREF_GRD) - val = PIN_VREFGRD; - } - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, val); -} - -/* - * Append the given mixer and verb elements for the later use - * The mixer array is referred in build_controls(), and init_verbs are - * called in init(). - */ -static void add_mixer(struct alc_spec *spec, const 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; -} - -/* - * GPIO setup tables, used in initialization - */ -/* Enable GPIO mask and set output */ -static const struct hda_verb alc_gpio1_init_verbs[] = { - {0x01, AC_VERB_SET_GPIO_MASK, 0x01}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x01}, - { } -}; - -static const struct hda_verb alc_gpio2_init_verbs[] = { - {0x01, AC_VERB_SET_GPIO_MASK, 0x02}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, - { } -}; - -static const struct hda_verb alc_gpio3_init_verbs[] = { - {0x01, AC_VERB_SET_GPIO_MASK, 0x03}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x03}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x03}, - { } -}; - -/* - * Fix hardware PLL issue - * On some codecs, the analog PLL gating control must be off while - * the default value is 1. - */ -static void alc_fix_pll(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - unsigned int val; - - if (!spec->pll_nid) - return; - snd_hda_codec_write(codec, spec->pll_nid, 0, AC_VERB_SET_COEF_INDEX, - spec->pll_coef_idx); - val = snd_hda_codec_read(codec, spec->pll_nid, 0, - AC_VERB_GET_PROC_COEF, 0); - snd_hda_codec_write(codec, spec->pll_nid, 0, AC_VERB_SET_COEF_INDEX, - spec->pll_coef_idx); - snd_hda_codec_write(codec, spec->pll_nid, 0, AC_VERB_SET_PROC_COEF, - val & ~(1 << spec->pll_coef_bit)); -} - -static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid, - unsigned int coef_idx, unsigned int coef_bit) -{ - struct alc_spec *spec = codec->spec; - spec->pll_nid = nid; - spec->pll_coef_idx = coef_idx; - spec->pll_coef_bit = coef_bit; - alc_fix_pll(codec); -} - -/* - * Jack detections for HP auto-mute and mic-switch - */ - -/* check each pin in the given array; returns true if any of them is plugged */ -static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) -{ - int i, present = 0; - - for (i = 0; i < num_pins; i++) { - hda_nid_t nid = pins[i]; - if (!nid) - break; - present |= snd_hda_jack_detect(codec, nid); - } - return present; -} - -/* standard HP/line-out auto-mute helper */ -static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, - bool mute, bool hp_out) -{ - struct alc_spec *spec = codec->spec; - unsigned int mute_bits = mute ? HDA_AMP_MUTE : 0; - unsigned int pin_bits = mute ? 0 : (hp_out ? PIN_HP : PIN_OUT); - int i; - - for (i = 0; i < num_pins; i++) { - hda_nid_t nid = pins[i]; - unsigned int val; - if (!nid) - break; - switch (spec->automute_mode) { - case ALC_AUTOMUTE_PIN: - /* don't reset VREF value in case it's controlling - * the amp (see alc861_fixup_asus_amp_vref_0f()) - */ - if (spec->keep_vref_in_automute) { - val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - val &= ~PIN_HP; - } else - val = 0; - val |= pin_bits; - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - val); - break; - case ALC_AUTOMUTE_AMP: - snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, - HDA_AMP_MUTE, mute_bits); - break; - case ALC_AUTOMUTE_MIXER: - nid = spec->automute_mixer_nid[i]; - if (!nid) - break; - snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 0, - HDA_AMP_MUTE, mute_bits); - snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 1, - HDA_AMP_MUTE, mute_bits); - break; - } - } -} - -/* Toggle outputs muting */ -static void update_outputs(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int on; - - /* Control HP pins/amps depending on master_mute state; - * in general, HP pins/amps control should be enabled in all cases, - * but currently set only for master_mute, just to be safe - */ - if (!spec->shared_mic_hp) /* don't change HP-pin when shared with mic */ - do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins), - spec->autocfg.hp_pins, spec->master_mute, true); - - if (!spec->automute_speaker) - on = 0; - else - on = spec->hp_jack_present | spec->line_jack_present; - on |= spec->master_mute; - do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins), - spec->autocfg.speaker_pins, on, false); - - /* toggle line-out mutes if needed, too */ - /* if LO is a copy of either HP or Speaker, don't need to handle it */ - if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0] || - spec->autocfg.line_out_pins[0] == spec->autocfg.speaker_pins[0]) - return; - if (!spec->automute_lo) - on = 0; - else - on = spec->hp_jack_present; - on |= spec->master_mute; - do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), - spec->autocfg.line_out_pins, on, false); -} - -static void call_update_outputs(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - if (spec->automute_hook) - spec->automute_hook(codec); - else - update_outputs(codec); -} - -/* standard HP-automute helper */ -static void alc_hp_automute(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->hp_jack_present = - detect_jacks(codec, ARRAY_SIZE(spec->autocfg.hp_pins), - spec->autocfg.hp_pins); - if (!spec->detect_hp || (!spec->automute_speaker && !spec->automute_lo)) - return; - call_update_outputs(codec); -} - -/* standard line-out-automute helper */ -static void alc_line_automute(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - /* check LO jack only when it's different from HP */ - if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0]) - return; - - spec->line_jack_present = - detect_jacks(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), - spec->autocfg.line_out_pins); - if (!spec->automute_speaker || !spec->detect_lo) - return; - call_update_outputs(codec); -} - -#define get_connection_index(codec, mux, nid) \ - snd_hda_get_conn_index(codec, mux, nid, 0) - -/* standard mic auto-switch helper */ -static void alc_mic_automute(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t *pins = spec->imux_pins; - - if (!spec->auto_mic || !spec->auto_mic_valid_imux) - return; - if (snd_BUG_ON(!spec->adc_nids)) - return; - if (snd_BUG_ON(spec->int_mic_idx < 0 || spec->ext_mic_idx < 0)) - return; - - if (snd_hda_jack_detect(codec, pins[spec->ext_mic_idx])) - alc_mux_select(codec, 0, spec->ext_mic_idx, false); - else if (spec->dock_mic_idx >= 0 && - snd_hda_jack_detect(codec, pins[spec->dock_mic_idx])) - alc_mux_select(codec, 0, spec->dock_mic_idx, false); - else - alc_mux_select(codec, 0, spec->int_mic_idx, false); -} - -/* handle the specified unsol action (ALC_XXX_EVENT) */ -static void alc_exec_unsol_event(struct hda_codec *codec, int action) -{ - switch (action) { - case ALC_HP_EVENT: - alc_hp_automute(codec); - break; - case ALC_FRONT_EVENT: - alc_line_automute(codec); - break; - case ALC_MIC_EVENT: - alc_mic_automute(codec); - break; - } - snd_hda_jack_report_sync(codec); -} - -/* update the master volume per volume-knob's unsol event */ -static void alc_update_knob_master(struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int val; - struct snd_kcontrol *kctl; - struct snd_ctl_elem_value *uctl; - - kctl = snd_hda_find_mixer_ctl(codec, "Master Playback Volume"); - if (!kctl) - return; - uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); - if (!uctl) - return; - val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_VOLUME_KNOB_CONTROL, 0); - val &= HDA_AMP_VOLMASK; - uctl->value.integer.value[0] = val; - uctl->value.integer.value[1] = val; - kctl->put(kctl, uctl); - kfree(uctl); -} - -/* unsolicited event for HP jack sensing */ -static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res) -{ - int action; - - if (codec->vendor_id == 0x10ec0880) - res >>= 28; - else - res >>= 26; - action = snd_hda_jack_get_action(codec, res); - if (action == ALC_DCVOL_EVENT) { - /* Execute the dc-vol event here as it requires the NID - * but we don't pass NID to alc_exec_unsol_event(). - * Once when we convert all static quirks to the auto-parser, - * this can be integerated into there. - */ - struct hda_jack_tbl *jack; - jack = snd_hda_jack_tbl_get_from_tag(codec, res); - if (jack) - alc_update_knob_master(codec, jack->nid); - return; - } - alc_exec_unsol_event(codec, action); -} - -/* call init functions of standard auto-mute helpers */ -static void alc_inithook(struct hda_codec *codec) -{ - alc_hp_automute(codec); - alc_line_automute(codec); - alc_mic_automute(codec); -} - -/* additional initialization for ALC888 variants */ -static void alc888_coef_init(struct hda_codec *codec) -{ - unsigned int tmp; - - snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0); - tmp = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_PROC_COEF, 0); - snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 7); - if ((tmp & 0xf0) == 0x20) - /* alc888S-VC */ - snd_hda_codec_read(codec, 0x20, 0, - AC_VERB_SET_PROC_COEF, 0x830); - else - /* alc888-VB */ - snd_hda_codec_read(codec, 0x20, 0, - AC_VERB_SET_PROC_COEF, 0x3030); -} - -/* additional initialization for ALC889 variants */ -static void alc889_coef_init(struct hda_codec *codec) -{ - unsigned int tmp; - - snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 7); - tmp = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_PROC_COEF, 0); - snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 7); - snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, tmp|0x2010); -} - -/* turn on/off EAPD control (only if available) */ -static void set_eapd(struct hda_codec *codec, hda_nid_t nid, int on) -{ - if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) - return; - if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD) - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE, - on ? 2 : 0); -} - -/* turn on/off EAPD controls of the codec */ -static void alc_auto_setup_eapd(struct hda_codec *codec, bool on) -{ - /* We currently only handle front, HP */ - static hda_nid_t pins[] = { - 0x0f, 0x10, 0x14, 0x15, 0 - }; - hda_nid_t *p; - for (p = pins; *p; p++) - set_eapd(codec, *p, on); -} - -/* generic shutup callback; - * just turning off EPAD and a little pause for avoiding pop-noise - */ -static void alc_eapd_shutup(struct hda_codec *codec) -{ - alc_auto_setup_eapd(codec, false); - msleep(200); -} - -/* generic EAPD initialization */ -static void alc_auto_init_amp(struct hda_codec *codec, int type) -{ - unsigned int tmp; - - alc_auto_setup_eapd(codec, true); - switch (type) { - case ALC_INIT_GPIO1: - snd_hda_sequence_write(codec, alc_gpio1_init_verbs); - break; - case ALC_INIT_GPIO2: - snd_hda_sequence_write(codec, alc_gpio2_init_verbs); - break; - case ALC_INIT_GPIO3: - snd_hda_sequence_write(codec, alc_gpio3_init_verbs); - break; - case ALC_INIT_DEFAULT: - switch (codec->vendor_id) { - case 0x10ec0260: - snd_hda_codec_write(codec, 0x1a, 0, - AC_VERB_SET_COEF_INDEX, 7); - tmp = snd_hda_codec_read(codec, 0x1a, 0, - AC_VERB_GET_PROC_COEF, 0); - snd_hda_codec_write(codec, 0x1a, 0, - AC_VERB_SET_COEF_INDEX, 7); - snd_hda_codec_write(codec, 0x1a, 0, - AC_VERB_SET_PROC_COEF, - tmp | 0x2010); - break; - case 0x10ec0262: - case 0x10ec0880: - case 0x10ec0882: - case 0x10ec0883: - case 0x10ec0885: - case 0x10ec0887: - /*case 0x10ec0889:*/ /* this causes an SPDIF problem */ - alc889_coef_init(codec); - break; - case 0x10ec0888: - alc888_coef_init(codec); - break; -#if 0 /* XXX: This may cause the silent output on speaker on some machines */ - case 0x10ec0267: - case 0x10ec0268: - snd_hda_codec_write(codec, 0x20, 0, - AC_VERB_SET_COEF_INDEX, 7); - tmp = snd_hda_codec_read(codec, 0x20, 0, - AC_VERB_GET_PROC_COEF, 0); - snd_hda_codec_write(codec, 0x20, 0, - AC_VERB_SET_COEF_INDEX, 7); - snd_hda_codec_write(codec, 0x20, 0, - AC_VERB_SET_PROC_COEF, - tmp | 0x3000); - break; -#endif /* XXX */ - } - break; - } -} - -/* - * Auto-Mute mode mixer enum support - */ -static int alc_automute_mode_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; - static const char * const texts2[] = { - "Disabled", "Enabled" - }; - static const char * const texts3[] = { - "Disabled", "Speaker Only", "Line Out+Speaker" - }; - const char * const *texts; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - if (spec->automute_speaker_possible && spec->automute_lo_possible) { - uinfo->value.enumerated.items = 3; - texts = texts3; - } else { - uinfo->value.enumerated.items = 2; - texts = texts2; - } - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; -} - -static int alc_automute_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - unsigned int val = 0; - if (spec->automute_speaker) - val++; - if (spec->automute_lo) - val++; - - ucontrol->value.enumerated.item[0] = val; - return 0; -} - -static int alc_automute_mode_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; - - switch (ucontrol->value.enumerated.item[0]) { - case 0: - if (!spec->automute_speaker && !spec->automute_lo) - return 0; - spec->automute_speaker = 0; - spec->automute_lo = 0; - break; - case 1: - if (spec->automute_speaker_possible) { - if (!spec->automute_lo && spec->automute_speaker) - return 0; - spec->automute_speaker = 1; - spec->automute_lo = 0; - } else if (spec->automute_lo_possible) { - if (spec->automute_lo) - return 0; - spec->automute_lo = 1; - } else - return -EINVAL; - break; - case 2: - if (!spec->automute_lo_possible || !spec->automute_speaker_possible) - return -EINVAL; - if (spec->automute_speaker && spec->automute_lo) - return 0; - spec->automute_speaker = 1; - spec->automute_lo = 1; - break; - default: - return -EINVAL; - } - call_update_outputs(codec); - return 1; -} - -static const struct snd_kcontrol_new alc_automute_mode_enum = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Auto-Mute Mode", - .info = alc_automute_mode_info, - .get = alc_automute_mode_get, - .put = alc_automute_mode_put, -}; - -static struct snd_kcontrol_new *alc_kcontrol_new(struct alc_spec *spec) -{ - snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); - return snd_array_new(&spec->kctls); -} - -static int alc_add_automute_mode_enum(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct snd_kcontrol_new *knew; - - knew = alc_kcontrol_new(spec); - if (!knew) - return -ENOMEM; - *knew = alc_automute_mode_enum; - knew->name = kstrdup("Auto-Mute Mode", GFP_KERNEL); - if (!knew->name) - return -ENOMEM; - return 0; -} - -/* - * Check the availability of HP/line-out auto-mute; - * Set up appropriately if really supported - */ -static void alc_init_automute(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int present = 0; - int i; - - if (cfg->hp_pins[0]) - present++; - if (cfg->line_out_pins[0]) - present++; - if (cfg->speaker_pins[0]) - present++; - if (present < 2) /* need two different output types */ - return; - - if (!cfg->speaker_pins[0] && - cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { - memcpy(cfg->speaker_pins, cfg->line_out_pins, - sizeof(cfg->speaker_pins)); - cfg->speaker_outs = cfg->line_outs; - } - - if (!cfg->hp_pins[0] && - cfg->line_out_type == AUTO_PIN_HP_OUT) { - memcpy(cfg->hp_pins, cfg->line_out_pins, - sizeof(cfg->hp_pins)); - cfg->hp_outs = cfg->line_outs; - } - - spec->automute_mode = ALC_AUTOMUTE_PIN; - - for (i = 0; i < cfg->hp_outs; i++) { - hda_nid_t nid = cfg->hp_pins[i]; - if (!is_jack_detectable(codec, nid)) - continue; - snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n", - nid); - snd_hda_jack_detect_enable(codec, nid, ALC_HP_EVENT); - spec->detect_hp = 1; - } - - if (cfg->line_out_type == AUTO_PIN_LINE_OUT && cfg->line_outs) { - if (cfg->speaker_outs) - for (i = 0; i < cfg->line_outs; i++) { - hda_nid_t nid = cfg->line_out_pins[i]; - if (!is_jack_detectable(codec, nid)) - continue; - snd_printdd("realtek: Enable Line-Out " - "auto-muting on NID 0x%x\n", nid); - snd_hda_jack_detect_enable(codec, nid, - ALC_FRONT_EVENT); - spec->detect_lo = 1; - } - spec->automute_lo_possible = spec->detect_hp; - } - - spec->automute_speaker_possible = cfg->speaker_outs && - (spec->detect_hp || spec->detect_lo); - - spec->automute_lo = spec->automute_lo_possible; - spec->automute_speaker = spec->automute_speaker_possible; - - if (spec->automute_speaker_possible || spec->automute_lo_possible) { - /* create a control for automute mode */ - alc_add_automute_mode_enum(codec); - spec->unsol_event = alc_sku_unsol_event; - } -} - -/* return the position of NID in the list, or -1 if not found */ -static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) -{ - int i; - for (i = 0; i < nums; i++) - if (list[i] == nid) - return i; - return -1; -} - -/* check whether dynamic ADC-switching is available */ -static bool alc_check_dyn_adc_switch(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux[0]; - int i, n, idx; - hda_nid_t cap, pin; - - if (imux != spec->input_mux) /* no dynamic imux? */ - return false; - - for (n = 0; n < spec->num_adc_nids; n++) { - cap = spec->private_capsrc_nids[n]; - for (i = 0; i < imux->num_items; i++) { - pin = spec->imux_pins[i]; - if (!pin) - return false; - if (get_connection_index(codec, cap, pin) < 0) - break; - } - if (i >= imux->num_items) - return true; /* no ADC-switch is needed */ - } - - for (i = 0; i < imux->num_items; i++) { - pin = spec->imux_pins[i]; - for (n = 0; n < spec->num_adc_nids; n++) { - cap = spec->private_capsrc_nids[n]; - idx = get_connection_index(codec, cap, pin); - if (idx >= 0) { - imux->items[i].index = idx; - spec->dyn_adc_idx[i] = n; - break; - } - } - } - - snd_printdd("realtek: enabling ADC switching\n"); - spec->dyn_adc_switch = 1; - return true; -} - -/* check whether all auto-mic pins are valid; setup indices if OK */ -static bool alc_auto_mic_check_imux(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - const struct hda_input_mux *imux; - - if (!spec->auto_mic) - return false; - if (spec->auto_mic_valid_imux) - return true; /* already checked */ - - /* fill up imux indices */ - if (!alc_check_dyn_adc_switch(codec)) { - spec->auto_mic = 0; - return false; - } - - imux = spec->input_mux; - spec->ext_mic_idx = find_idx_in_nid_list(spec->ext_mic_pin, - spec->imux_pins, imux->num_items); - spec->int_mic_idx = find_idx_in_nid_list(spec->int_mic_pin, - spec->imux_pins, imux->num_items); - spec->dock_mic_idx = find_idx_in_nid_list(spec->dock_mic_pin, - spec->imux_pins, imux->num_items); - if (spec->ext_mic_idx < 0 || spec->int_mic_idx < 0) { - spec->auto_mic = 0; - return false; /* no corresponding imux */ - } - - snd_hda_jack_detect_enable(codec, spec->ext_mic_pin, ALC_MIC_EVENT); - if (spec->dock_mic_pin) - snd_hda_jack_detect_enable(codec, spec->dock_mic_pin, - ALC_MIC_EVENT); - - spec->auto_mic_valid_imux = 1; - spec->auto_mic = 1; - return true; -} - -/* - * Check the availability of auto-mic switch; - * Set up if really supported - */ -static void alc_init_auto_mic(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t fixed, ext, dock; - int i; - - if (spec->shared_mic_hp) - return; /* no auto-mic for the shared I/O */ - - spec->ext_mic_idx = spec->int_mic_idx = spec->dock_mic_idx = -1; - - fixed = ext = dock = 0; - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - unsigned int defcfg; - defcfg = snd_hda_codec_get_pincfg(codec, nid); - switch (snd_hda_get_input_pin_attr(defcfg)) { - case INPUT_PIN_ATTR_INT: - if (fixed) - return; /* already occupied */ - if (cfg->inputs[i].type != AUTO_PIN_MIC) - return; /* invalid type */ - fixed = nid; - break; - case INPUT_PIN_ATTR_UNUSED: - return; /* invalid entry */ - case INPUT_PIN_ATTR_DOCK: - if (dock) - return; /* already occupied */ - if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) - return; /* invalid type */ - dock = nid; - break; - default: - if (ext) - return; /* already occupied */ - if (cfg->inputs[i].type != AUTO_PIN_MIC) - return; /* invalid type */ - ext = nid; - break; - } - } - if (!ext && dock) { - ext = dock; - dock = 0; - } - if (!ext || !fixed) - return; - if (!is_jack_detectable(codec, ext)) - return; /* no unsol support */ - if (dock && !is_jack_detectable(codec, dock)) - return; /* no unsol support */ - - /* check imux indices */ - spec->ext_mic_pin = ext; - spec->int_mic_pin = fixed; - spec->dock_mic_pin = dock; - - spec->auto_mic = 1; - if (!alc_auto_mic_check_imux(codec)) - return; - - snd_printdd("realtek: Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n", - ext, fixed, dock); - spec->unsol_event = alc_sku_unsol_event; -} - -/* check the availabilities of auto-mute and auto-mic switches */ -static void alc_auto_check_switches(struct hda_codec *codec) -{ - alc_init_automute(codec); - alc_init_auto_mic(codec); -} - -/* - * Realtek SSID verification - */ - -/* Could be any non-zero and even value. When used as fixup, tells - * the driver to ignore any present sku defines. - */ -#define ALC_FIXUP_SKU_IGNORE (2) - -static int alc_auto_parse_customize_define(struct hda_codec *codec) -{ - unsigned int ass, tmp, i; - unsigned nid = 0; - struct alc_spec *spec = codec->spec; - - spec->cdefine.enable_pcbeep = 1; /* assume always enabled */ - - if (spec->cdefine.fixup) { - ass = spec->cdefine.sku_cfg; - if (ass == ALC_FIXUP_SKU_IGNORE) - return -1; - goto do_sku; - } - - ass = codec->subsystem_id & 0xffff; - if (ass != codec->bus->pci->subsystem_device && (ass & 1)) - goto do_sku; - - nid = 0x1d; - if (codec->vendor_id == 0x10ec0260) - nid = 0x17; - ass = snd_hda_codec_get_pincfg(codec, nid); - - if (!(ass & 1)) { - printk(KERN_INFO "hda_codec: %s: SKU not ready 0x%08x\n", - codec->chip_name, ass); - return -1; - } - - /* check sum */ - tmp = 0; - for (i = 1; i < 16; i++) { - if ((ass >> i) & 1) - tmp++; - } - if (((ass >> 16) & 0xf) != tmp) - return -1; - - spec->cdefine.port_connectivity = ass >> 30; - spec->cdefine.enable_pcbeep = (ass & 0x100000) >> 20; - spec->cdefine.check_sum = (ass >> 16) & 0xf; - spec->cdefine.customization = ass >> 8; -do_sku: - spec->cdefine.sku_cfg = ass; - spec->cdefine.external_amp = (ass & 0x38) >> 3; - spec->cdefine.platform_type = (ass & 0x4) >> 2; - spec->cdefine.swap = (ass & 0x2) >> 1; - spec->cdefine.override = ass & 0x1; - - snd_printd("SKU: Nid=0x%x sku_cfg=0x%08x\n", - nid, spec->cdefine.sku_cfg); - snd_printd("SKU: port_connectivity=0x%x\n", - spec->cdefine.port_connectivity); - snd_printd("SKU: enable_pcbeep=0x%x\n", spec->cdefine.enable_pcbeep); - snd_printd("SKU: check_sum=0x%08x\n", spec->cdefine.check_sum); - snd_printd("SKU: customization=0x%08x\n", spec->cdefine.customization); - snd_printd("SKU: external_amp=0x%x\n", spec->cdefine.external_amp); - snd_printd("SKU: platform_type=0x%x\n", spec->cdefine.platform_type); - snd_printd("SKU: swap=0x%x\n", spec->cdefine.swap); - snd_printd("SKU: override=0x%x\n", spec->cdefine.override); - - return 0; -} - -/* return true if the given NID is found in the list */ -static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) -{ - return find_idx_in_nid_list(nid, list, nums) >= 0; -} - -/* check subsystem ID and set up device-specific initialization; - * return 1 if initialized, 0 if invalid SSID - */ -/* 32-bit subsystem ID for BIOS loading in HD Audio codec. - * 31 ~ 16 : Manufacture ID - * 15 ~ 8 : SKU ID - * 7 ~ 0 : Assembly ID - * port-A --> pin 39/41, port-E --> pin 14/15, port-D --> pin 35/36 - */ -static int alc_subsystem_id(struct hda_codec *codec, - hda_nid_t porta, hda_nid_t porte, - hda_nid_t portd, hda_nid_t porti) -{ - unsigned int ass, tmp, i; - unsigned nid; - struct alc_spec *spec = codec->spec; - - if (spec->cdefine.fixup) { - ass = spec->cdefine.sku_cfg; - if (ass == ALC_FIXUP_SKU_IGNORE) - return 0; - goto do_sku; - } - - ass = codec->subsystem_id & 0xffff; - if ((ass != codec->bus->pci->subsystem_device) && (ass & 1)) - goto do_sku; - - /* invalid SSID, check the special NID pin defcfg instead */ - /* - * 31~30 : port connectivity - * 29~21 : reserve - * 20 : PCBEEP input - * 19~16 : Check sum (15:1) - * 15~1 : Custom - * 0 : override - */ - nid = 0x1d; - if (codec->vendor_id == 0x10ec0260) - nid = 0x17; - ass = snd_hda_codec_get_pincfg(codec, nid); - snd_printd("realtek: No valid SSID, " - "checking pincfg 0x%08x for NID 0x%x\n", - ass, nid); - if (!(ass & 1)) - return 0; - if ((ass >> 30) != 1) /* no physical connection */ - return 0; - - /* check sum */ - tmp = 0; - for (i = 1; i < 16; i++) { - if ((ass >> i) & 1) - tmp++; - } - if (((ass >> 16) & 0xf) != tmp) - return 0; -do_sku: - snd_printd("realtek: Enabling init ASM_ID=0x%04x CODEC_ID=%08x\n", - ass & 0xffff, codec->vendor_id); - /* - * 0 : override - * 1 : Swap Jack - * 2 : 0 --> Desktop, 1 --> Laptop - * 3~5 : External Amplifier control - * 7~6 : Reserved - */ - tmp = (ass & 0x38) >> 3; /* external Amp control */ - switch (tmp) { - case 1: - spec->init_amp = ALC_INIT_GPIO1; - break; - case 3: - spec->init_amp = ALC_INIT_GPIO2; - break; - case 7: - spec->init_amp = ALC_INIT_GPIO3; - break; - case 5: - default: - spec->init_amp = ALC_INIT_DEFAULT; - break; - } - - /* is laptop or Desktop and enable the function "Mute internal speaker - * when the external headphone out jack is plugged" - */ - if (!(ass & 0x8000)) - return 1; - /* - * 10~8 : Jack location - * 12~11: Headphone out -> 00: PortA, 01: PortE, 02: PortD, 03: Resvered - * 14~13: Resvered - * 15 : 1 --> enable the function "Mute internal speaker - * when the external headphone out jack is plugged" - */ - if (!spec->autocfg.hp_pins[0] && - !(spec->autocfg.line_out_pins[0] && - spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)) { - hda_nid_t nid; - tmp = (ass >> 11) & 0x3; /* HP to chassis */ - if (tmp == 0) - nid = porta; - else if (tmp == 1) - nid = porte; - else if (tmp == 2) - nid = portd; - else if (tmp == 3) - nid = porti; - else - return 1; - if (found_in_nid_list(nid, spec->autocfg.line_out_pins, - spec->autocfg.line_outs)) - return 1; - spec->autocfg.hp_pins[0] = nid; - } - return 1; -} - -/* Check the validity of ALC subsystem-id - * ports contains an array of 4 pin NIDs for port-A, E, D and I */ -static void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports) -{ - if (!alc_subsystem_id(codec, ports[0], ports[1], ports[2], ports[3])) { - struct alc_spec *spec = codec->spec; - snd_printd("realtek: " - "Enable default setup for auto mode as fallback\n"); - spec->init_amp = ALC_INIT_DEFAULT; - } -} - -/* - * Fix-up pin default configurations and add default verbs - */ - -struct alc_pincfg { - hda_nid_t nid; - u32 val; -}; - -struct alc_model_fixup { - const int id; - const char *name; -}; - -struct alc_fixup { - int type; - bool chained; - int chain_id; - union { - unsigned int sku; - const struct alc_pincfg *pins; - const struct hda_verb *verbs; - void (*func)(struct hda_codec *codec, - const struct alc_fixup *fix, - int action); - } v; -}; - -enum { - ALC_FIXUP_INVALID, - ALC_FIXUP_SKU, - ALC_FIXUP_PINS, - ALC_FIXUP_VERBS, - ALC_FIXUP_FUNC, -}; - -enum { - ALC_FIXUP_ACT_PRE_PROBE, - ALC_FIXUP_ACT_PROBE, - ALC_FIXUP_ACT_INIT, - ALC_FIXUP_ACT_BUILD, -}; - -static void alc_apply_pincfgs(struct hda_codec *codec, - const struct alc_pincfg *cfg) -{ - for (; cfg->nid; cfg++) - snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val); -} - -static void alc_apply_fixup(struct hda_codec *codec, int action) -{ - struct alc_spec *spec = codec->spec; - int id = spec->fixup_id; -#ifdef CONFIG_SND_DEBUG_VERBOSE - const char *modelname = spec->fixup_name; -#endif - int depth = 0; - - if (!spec->fixup_list) - return; - - while (id >= 0) { - const struct alc_fixup *fix = spec->fixup_list + id; - const struct alc_pincfg *cfg; - - switch (fix->type) { - case ALC_FIXUP_SKU: - if (action != ALC_FIXUP_ACT_PRE_PROBE || !fix->v.sku) - break; - snd_printdd(KERN_INFO "hda_codec: %s: " - "Apply sku override for %s\n", - codec->chip_name, modelname); - spec->cdefine.sku_cfg = fix->v.sku; - spec->cdefine.fixup = 1; - break; - case ALC_FIXUP_PINS: - cfg = fix->v.pins; - if (action != ALC_FIXUP_ACT_PRE_PROBE || !cfg) - break; - snd_printdd(KERN_INFO "hda_codec: %s: " - "Apply pincfg for %s\n", - codec->chip_name, modelname); - alc_apply_pincfgs(codec, cfg); - break; - case ALC_FIXUP_VERBS: - if (action != ALC_FIXUP_ACT_PROBE || !fix->v.verbs) - break; - snd_printdd(KERN_INFO "hda_codec: %s: " - "Apply fix-verbs for %s\n", - codec->chip_name, modelname); - add_verb(codec->spec, fix->v.verbs); - break; - case ALC_FIXUP_FUNC: - if (!fix->v.func) - break; - snd_printdd(KERN_INFO "hda_codec: %s: " - "Apply fix-func for %s\n", - codec->chip_name, modelname); - fix->v.func(codec, fix, action); - break; - default: - snd_printk(KERN_ERR "hda_codec: %s: " - "Invalid fixup type %d\n", - codec->chip_name, fix->type); - break; - } - if (!fix->chained) - break; - if (++depth > 10) - break; - id = fix->chain_id; - } -} - -static void alc_pick_fixup(struct hda_codec *codec, - const struct alc_model_fixup *models, - const struct snd_pci_quirk *quirk, - const struct alc_fixup *fixlist) -{ - struct alc_spec *spec = codec->spec; - const struct snd_pci_quirk *q; - int id = -1; - const char *name = NULL; - - /* when model=nofixup is given, don't pick up any fixups */ - if (codec->modelname && !strcmp(codec->modelname, "nofixup")) { - spec->fixup_list = NULL; - spec->fixup_id = -1; - return; - } - - if (codec->modelname && models) { - while (models->name) { - if (!strcmp(codec->modelname, models->name)) { - id = models->id; - name = models->name; - break; - } - models++; - } - } - if (id < 0) { - q = snd_pci_quirk_lookup(codec->bus->pci, quirk); - if (q) { - id = q->value; -#ifdef CONFIG_SND_DEBUG_VERBOSE - name = q->name; -#endif - } - } - if (id < 0) { - for (q = quirk; q->subvendor; q++) { - unsigned int vendorid = - q->subdevice | (q->subvendor << 16); - if (vendorid == codec->subsystem_id) { - id = q->value; -#ifdef CONFIG_SND_DEBUG_VERBOSE - name = q->name; -#endif - break; - } - } - } - - spec->fixup_id = id; - if (id >= 0) { - spec->fixup_list = fixlist; - spec->fixup_name = name; - } -} - -/* - * COEF access helper functions - */ -static int alc_read_coef_idx(struct hda_codec *codec, - unsigned int coef_idx) -{ - unsigned int val; - snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, - coef_idx); - val = snd_hda_codec_read(codec, 0x20, 0, - AC_VERB_GET_PROC_COEF, 0); - return val; -} - -static void alc_write_coef_idx(struct hda_codec *codec, unsigned int coef_idx, - unsigned int coef_val) -{ - snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, - coef_idx); - snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, - coef_val); -} - -/* a special bypass for COEF 0; read the cached value at the second time */ -static unsigned int alc_get_coef0(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - if (!spec->coef0) - spec->coef0 = alc_read_coef_idx(codec, 0); - return spec->coef0; -} - -/* - * Digital I/O handling - */ - -/* set right pin controls for digital I/O */ -static void alc_auto_init_digital(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int i; - hda_nid_t pin, dac; - - for (i = 0; i < spec->autocfg.dig_outs; i++) { - pin = spec->autocfg.dig_out_pins[i]; - if (!pin) - continue; - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - if (!i) - dac = spec->multiout.dig_out_nid; - else - dac = spec->slave_dig_outs[i - 1]; - if (!dac || !(get_wcaps(codec, dac) & AC_WCAP_OUT_AMP)) - continue; - snd_hda_codec_write(codec, dac, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); - } - pin = spec->autocfg.dig_in_pin; - if (pin) - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - PIN_IN); -} - -/* parse digital I/Os and set up NIDs in BIOS auto-parse mode */ -static void alc_auto_parse_digital(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int i, err, nums; - hda_nid_t dig_nid; - - /* support multiple SPDIFs; the secondary is set up as a slave */ - nums = 0; - for (i = 0; i < spec->autocfg.dig_outs; i++) { - hda_nid_t conn[4]; - err = snd_hda_get_connections(codec, - spec->autocfg.dig_out_pins[i], - conn, ARRAY_SIZE(conn)); - if (err <= 0) - continue; - dig_nid = conn[0]; /* assume the first element is audio-out */ - if (!nums) { - spec->multiout.dig_out_nid = dig_nid; - spec->dig_out_type = spec->autocfg.dig_out_type[0]; - } else { - spec->multiout.slave_dig_outs = spec->slave_dig_outs; - if (nums >= ARRAY_SIZE(spec->slave_dig_outs) - 1) - break; - spec->slave_dig_outs[nums - 1] = dig_nid; - } - nums++; - } - - if (spec->autocfg.dig_in_pin) { - dig_nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, dig_nid++) { - unsigned int wcaps = get_wcaps(codec, dig_nid); - if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) - continue; - if (!(wcaps & AC_WCAP_DIGITAL)) - continue; - if (!(wcaps & AC_WCAP_CONN_LIST)) - continue; - err = get_connection_index(codec, dig_nid, - spec->autocfg.dig_in_pin); - if (err >= 0) { - spec->dig_in_nid = dig_nid; - break; - } - } - } -} - -/* - * capture mixer elements - */ -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; - unsigned long val; - int err; - - mutex_lock(&codec->control_mutex); - if (spec->vol_in_capsrc) - val = HDA_COMPOSE_AMP_VAL(spec->capsrc_nids[0], 3, 0, HDA_OUTPUT); - else - val = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0, HDA_INPUT); - kcontrol->private_value = val; - err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo); - mutex_unlock(&codec->control_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; - unsigned long val; - int err; - - mutex_lock(&codec->control_mutex); - if (spec->vol_in_capsrc) - val = HDA_COMPOSE_AMP_VAL(spec->capsrc_nids[0], 3, 0, HDA_OUTPUT); - else - val = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0, HDA_INPUT); - kcontrol->private_value = val; - err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv); - mutex_unlock(&codec->control_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, bool check_adc_switch) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - int i, err = 0; - - mutex_lock(&codec->control_mutex); - if (check_adc_switch && spec->dyn_adc_switch) { - for (i = 0; i < spec->num_adc_nids; i++) { - kcontrol->private_value = - HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], - 3, 0, HDA_INPUT); - err = func(kcontrol, ucontrol); - if (err < 0) - goto error; - } - } else { - i = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - if (spec->vol_in_capsrc) - kcontrol->private_value = - HDA_COMPOSE_AMP_VAL(spec->capsrc_nids[i], - 3, 0, HDA_OUTPUT); - else - kcontrol->private_value = - HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], - 3, 0, HDA_INPUT); - err = func(kcontrol, ucontrol); - } - error: - mutex_unlock(&codec->control_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, false); -} - -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, true); -} - -/* 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, false); -} - -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, true); -} - -#define _DEFINE_CAPMIX(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 }, \ - } - -#define _DEFINE_CAPSRC(num) \ - { \ - .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, \ - } - -#define DEFINE_CAPMIX(num) \ -static const struct snd_kcontrol_new alc_capture_mixer ## num[] = { \ - _DEFINE_CAPMIX(num), \ - _DEFINE_CAPSRC(num), \ - { } /* end */ \ -} - -#define DEFINE_CAPMIX_NOSRC(num) \ -static const struct snd_kcontrol_new alc_capture_mixer_nosrc ## num[] = { \ - _DEFINE_CAPMIX(num), \ - { } /* end */ \ -} - -/* up to three ADCs */ -DEFINE_CAPMIX(1); -DEFINE_CAPMIX(2); -DEFINE_CAPMIX(3); -DEFINE_CAPMIX_NOSRC(1); -DEFINE_CAPMIX_NOSRC(2); -DEFINE_CAPMIX_NOSRC(3); - -/* - * virtual master controls - */ - -/* - * slave controls for virtual master - */ -static const char * const alc_slave_pfxs[] = { - "Front", "Surround", "Center", "LFE", "Side", - "Headphone", "Speaker", "Mono", "Line Out", - "CLFE", "Bass Speaker", "PCM", - NULL, -}; - -/* - * build control elements - */ - -#define NID_MAPPING (-1) - -#define SUBDEV_SPEAKER_ (0 << 6) -#define SUBDEV_HP_ (1 << 6) -#define SUBDEV_LINE_ (2 << 6) -#define SUBDEV_SPEAKER(x) (SUBDEV_SPEAKER_ | ((x) & 0x3f)) -#define SUBDEV_HP(x) (SUBDEV_HP_ | ((x) & 0x3f)) -#define SUBDEV_LINE(x) (SUBDEV_LINE_ | ((x) & 0x3f)) - -static void alc_free_kctls(struct hda_codec *codec); - -#ifdef CONFIG_SND_HDA_INPUT_BEEP -/* additional beep mixers; the actual parameters are overwritten at build */ -static const struct snd_kcontrol_new alc_beep_mixer[] = { - HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_INPUT), - HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_INPUT), - { } /* end */ -}; -#endif - -static int __alc_build_controls(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct snd_kcontrol *kctl = NULL; - const struct snd_kcontrol_new *knew; - int i, j, err; - unsigned int u; - hda_nid_t nid; - - for (i = 0; i < spec->num_mixers; i++) { - err = snd_hda_add_new_ctls(codec, spec->mixers[i]); - 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, - spec->multiout.dig_out_nid); - if (err < 0) - return err; - if (!spec->no_analog) { - err = snd_hda_create_spdif_share_sw(codec, - &spec->multiout); - if (err < 0) - return err; - spec->multiout.share_spdif = 1; - } - } - if (spec->dig_in_nid) { - err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); - if (err < 0) - return err; - } - -#ifdef CONFIG_SND_HDA_INPUT_BEEP - /* create beep controls if needed */ - if (spec->beep_amp) { - const struct snd_kcontrol_new *knew; - for (knew = alc_beep_mixer; knew->name; knew++) { - struct snd_kcontrol *kctl; - kctl = snd_ctl_new1(knew, codec); - if (!kctl) - return -ENOMEM; - kctl->private_value = spec->beep_amp; - err = snd_hda_ctl_add(codec, 0, kctl); - if (err < 0) - return err; - } - } -#endif - - /* if we have no master control, let's create it */ - if (!spec->no_analog && - !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { - unsigned int vmaster_tlv[4]; - snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, - HDA_OUTPUT, vmaster_tlv); - err = snd_hda_add_vmaster(codec, "Master Playback Volume", - vmaster_tlv, alc_slave_pfxs, - "Playback Volume"); - if (err < 0) - return err; - } - if (!spec->no_analog && - !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { - err = __snd_hda_add_vmaster(codec, "Master Playback Switch", - NULL, alc_slave_pfxs, - "Playback Switch", - true, &spec->vmaster_mute.sw_kctl); - if (err < 0) - return err; - } - - /* assign Capture Source enums to NID */ - if (spec->capsrc_nids || spec->adc_nids) { - kctl = snd_hda_find_mixer_ctl(codec, "Capture Source"); - if (!kctl) - kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); - for (i = 0; kctl && i < kctl->count; i++) { - err = snd_hda_add_nid(codec, kctl, i, - get_capsrc(spec, i)); - if (err < 0) - return err; - } - } - if (spec->cap_mixer && spec->adc_nids) { - const char *kname = kctl ? kctl->id.name : NULL; - for (knew = spec->cap_mixer; knew->name; knew++) { - if (kname && strcmp(knew->name, kname) == 0) - continue; - kctl = snd_hda_find_mixer_ctl(codec, knew->name); - for (i = 0; kctl && i < kctl->count; i++) { - err = snd_hda_add_nid(codec, kctl, i, - spec->adc_nids[i]); - if (err < 0) - return err; - } - } - } - - /* other nid->control mapping */ - for (i = 0; i < spec->num_mixers; i++) { - for (knew = spec->mixers[i]; knew->name; knew++) { - if (knew->iface != NID_MAPPING) - continue; - kctl = snd_hda_find_mixer_ctl(codec, knew->name); - if (kctl == NULL) - continue; - u = knew->subdevice; - for (j = 0; j < 4; j++, u >>= 8) { - nid = u & 0x3f; - if (nid == 0) - continue; - switch (u & 0xc0) { - case SUBDEV_SPEAKER_: - nid = spec->autocfg.speaker_pins[nid]; - break; - case SUBDEV_LINE_: - nid = spec->autocfg.line_out_pins[nid]; - break; - case SUBDEV_HP_: - nid = spec->autocfg.hp_pins[nid]; - break; - default: - continue; - } - err = snd_hda_add_nid(codec, kctl, 0, nid); - if (err < 0) - return err; - } - u = knew->private_value; - for (j = 0; j < 4; j++, u >>= 8) { - nid = u & 0xff; - if (nid == 0) - continue; - err = snd_hda_add_nid(codec, kctl, 0, nid); - if (err < 0) - return err; - } - } - } - - alc_free_kctls(codec); /* no longer needed */ - - return 0; -} - -static int alc_build_controls(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int err = __alc_build_controls(codec); - if (err < 0) - return err; - err = snd_hda_jack_add_kctls(codec, &spec->autocfg); - if (err < 0) - return err; - alc_apply_fixup(codec, ALC_FIXUP_ACT_BUILD); - return 0; -} - - -/* - * Common callbacks - */ - -static void alc_init_special_input_src(struct hda_codec *codec); -static void alc_auto_init_std(struct hda_codec *codec); - -static int alc_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - unsigned int i; - - if (spec->init_hook) - spec->init_hook(codec); - - alc_fix_pll(codec); - alc_auto_init_amp(codec, spec->init_amp); - - for (i = 0; i < spec->num_init_verbs; i++) - snd_hda_sequence_write(codec, spec->init_verbs[i]); - alc_init_special_input_src(codec); - alc_auto_init_std(codec); - - alc_apply_fixup(codec, ALC_FIXUP_ACT_INIT); - - snd_hda_jack_report_sync(codec); - - hda_call_check_power_status(codec, 0x01); - return 0; -} - -static void alc_unsol_event(struct hda_codec *codec, unsigned int res) -{ - struct alc_spec *spec = codec->spec; - - if (spec->unsol_event) - spec->unsol_event(codec, res); -} - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static int alc_check_power_status(struct hda_codec *codec, hda_nid_t nid) -{ - struct alc_spec *spec = codec->spec; - return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); -} -#endif - -/* - * Analog playback callbacks - */ -static int alc_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, - hinfo); -} - -static int alc_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 alc_spec *spec = codec->spec; - return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, - stream_tag, format, substream); -} - -static int alc_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); -} - -/* - * Digital out - */ -static int alc_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - return snd_hda_multi_out_dig_open(codec, &spec->multiout); -} - -static int alc_dig_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 alc_spec *spec = codec->spec; - return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, - stream_tag, format, substream); -} - -static int alc_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); -} - -static int alc_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} - -/* - * Analog capture - */ -static int alc_alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1], - stream_tag, 0, format); - return 0; -} - -static int alc_alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - - snd_hda_codec_cleanup_stream(codec, - spec->adc_nids[substream->number + 1]); - return 0; -} - -/* analog capture with dynamic dual-adc changes */ -static int dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - spec->cur_adc = spec->adc_nids[spec->dyn_adc_idx[spec->cur_mux[0]]]; - spec->cur_adc_stream_tag = stream_tag; - spec->cur_adc_format = format; - snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format); - return 0; -} - -static int dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - snd_hda_codec_cleanup_stream(codec, spec->cur_adc); - spec->cur_adc = 0; - return 0; -} - -static const struct hda_pcm_stream dyn_adc_pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .nid = 0, /* fill later */ - .ops = { - .prepare = dyn_adc_capture_pcm_prepare, - .cleanup = dyn_adc_capture_pcm_cleanup - }, -}; - -/* - */ -static const struct hda_pcm_stream alc_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 8, - /* NID is set in alc_build_pcms */ - .ops = { - .open = alc_playback_pcm_open, - .prepare = alc_playback_pcm_prepare, - .cleanup = alc_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream alc_pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in alc_build_pcms */ -}; - -static const struct hda_pcm_stream alc_pcm_analog_alt_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in alc_build_pcms */ -}; - -static const struct hda_pcm_stream alc_pcm_analog_alt_capture = { - .substreams = 2, /* can be overridden */ - .channels_min = 2, - .channels_max = 2, - /* NID is set in alc_build_pcms */ - .ops = { - .prepare = alc_alt_capture_pcm_prepare, - .cleanup = alc_alt_capture_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream alc_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in alc_build_pcms */ - .ops = { - .open = alc_dig_playback_pcm_open, - .close = alc_dig_playback_pcm_close, - .prepare = alc_dig_playback_pcm_prepare, - .cleanup = alc_dig_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream alc_pcm_digital_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in alc_build_pcms */ -}; - -/* Used by alc_build_pcms to flag that a PCM has no playback stream */ -static const struct hda_pcm_stream alc_pcm_null_stream = { - .substreams = 0, - .channels_min = 0, - .channels_max = 0, -}; - -static int alc_build_pcms(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; - const struct hda_pcm_stream *p; - bool have_multi_adcs; - int i; - - codec->num_pcms = 1; - codec->pcm_info = info; - - if (spec->no_analog) - goto skip_analog; - - snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog), - "%s Analog", codec->chip_name); - info->name = spec->stream_name_analog; - - if (spec->multiout.num_dacs > 0) { - p = spec->stream_analog_playback; - if (!p) - p = &alc_pcm_analog_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; - } - if (spec->adc_nids) { - p = spec->stream_analog_capture; - if (!p) { - if (spec->dyn_adc_switch) - p = &dyn_adc_pcm_analog_capture; - else - p = &alc_pcm_analog_capture; - } - info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; - } - - if (spec->channel_mode) { - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 0; - for (i = 0; i < spec->num_channel_mode; i++) { - if (spec->channel_mode[i].channels > info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max) { - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->channel_mode[i].channels; - } - } - } - - skip_analog: - /* SPDIF for stream index #1 */ - if (spec->multiout.dig_out_nid || spec->dig_in_nid) { - snprintf(spec->stream_name_digital, - sizeof(spec->stream_name_digital), - "%s Digital", codec->chip_name); - codec->num_pcms = 2; - codec->slave_dig_outs = spec->multiout.slave_dig_outs; - info = spec->pcm_rec + 1; - info->name = spec->stream_name_digital; - if (spec->dig_out_type) - info->pcm_type = spec->dig_out_type; - else - info->pcm_type = HDA_PCM_TYPE_SPDIF; - if (spec->multiout.dig_out_nid) { - p = spec->stream_digital_playback; - if (!p) - p = &alc_pcm_digital_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; - } - if (spec->dig_in_nid) { - p = spec->stream_digital_capture; - if (!p) - p = &alc_pcm_digital_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; - } - /* FIXME: do we need this for all Realtek codec models? */ - codec->spdif_status_reset = 1; - } - - if (spec->no_analog) - return 0; - - /* If the use of more than one ADC is requested for the current - * model, configure a second analog capture-only PCM. - */ - have_multi_adcs = (spec->num_adc_nids > 1) && - !spec->dyn_adc_switch && !spec->auto_mic && - (!spec->input_mux || spec->input_mux->num_items > 1); - /* Additional Analaog capture for index #2 */ - if (spec->alt_dac_nid || have_multi_adcs) { - codec->num_pcms = 3; - info = spec->pcm_rec + 2; - info->name = spec->stream_name_analog; - if (spec->alt_dac_nid) { - p = spec->stream_analog_alt_playback; - if (!p) - p = &alc_pcm_analog_alt_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = - spec->alt_dac_nid; - } else { - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = - alc_pcm_null_stream; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0; - } - if (have_multi_adcs) { - p = spec->stream_analog_alt_capture; - if (!p) - p = &alc_pcm_analog_alt_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = - spec->adc_nids[1]; - info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = - spec->num_adc_nids - 1; - } else { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = - alc_pcm_null_stream; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 0; - } - } - - return 0; -} - -static inline void alc_shutup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (spec && spec->shutup) - spec->shutup(codec); - snd_hda_shutup_pins(codec); -} - -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_bind_ctls(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - if (spec->bind_ctls.list) { - struct hda_bind_ctls **ctl = spec->bind_ctls.list; - int i; - for (i = 0; i < spec->bind_ctls.used; i++) - kfree(ctl[i]); - } - snd_array_free(&spec->bind_ctls); -} - -static void alc_free(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (!spec) - return; - - alc_shutup(codec); - alc_free_kctls(codec); - alc_free_bind_ctls(codec); - kfree(spec); - snd_hda_detach_beep_device(codec); -} - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static void alc_power_eapd(struct hda_codec *codec) -{ - alc_auto_setup_eapd(codec, false); -} - -static int alc_suspend(struct hda_codec *codec, pm_message_t state) -{ - struct alc_spec *spec = codec->spec; - alc_shutup(codec); - if (spec && spec->power_hook) - spec->power_hook(codec); - return 0; -} -#endif - -#ifdef CONFIG_PM -static int alc_resume(struct hda_codec *codec) -{ - msleep(150); /* to avoid pop noise */ - codec->patch_ops.init(codec); - snd_hda_codec_resume_amp(codec); - snd_hda_codec_resume_cache(codec); - hda_call_check_power_status(codec, 0x01); - return 0; -} -#endif - -/* - */ -static const struct hda_codec_ops alc_patch_ops = { - .build_controls = alc_build_controls, - .build_pcms = alc_build_pcms, - .init = alc_init, - .free = alc_free, - .unsol_event = alc_unsol_event, -#ifdef CONFIG_PM - .resume = alc_resume, -#endif -#ifdef CONFIG_SND_HDA_POWER_SAVE - .suspend = alc_suspend, - .check_power_status = alc_check_power_status, -#endif - .reboot_notify = alc_shutup, -}; - -/* replace the codec chip_name with the given string */ -static int alc_codec_rename(struct hda_codec *codec, const char *name) -{ - kfree(codec->chip_name); - codec->chip_name = kstrdup(name, GFP_KERNEL); - if (!codec->chip_name) { - alc_free(codec); - return -ENOMEM; - } - return 0; -} - -/* - * Rename codecs appropriately from COEF value - */ -struct alc_codec_rename_table { - unsigned int vendor_id; - unsigned short coef_mask; - unsigned short coef_bits; - const char *name; -}; - -static struct alc_codec_rename_table rename_tbl[] = { - { 0x10ec0269, 0xfff0, 0x3010, "ALC277" }, - { 0x10ec0269, 0xf0f0, 0x2010, "ALC259" }, - { 0x10ec0269, 0xf0f0, 0x3010, "ALC258" }, - { 0x10ec0269, 0x00f0, 0x0010, "ALC269VB" }, - { 0x10ec0269, 0xffff, 0xa023, "ALC259" }, - { 0x10ec0269, 0xffff, 0x6023, "ALC281X" }, - { 0x10ec0269, 0x00f0, 0x0020, "ALC269VC" }, - { 0x10ec0887, 0x00f0, 0x0030, "ALC887-VD" }, - { 0x10ec0888, 0x00f0, 0x0030, "ALC888-VD" }, - { 0x10ec0888, 0xf0f0, 0x3020, "ALC886" }, - { 0x10ec0899, 0x2000, 0x2000, "ALC899" }, - { 0x10ec0892, 0xffff, 0x8020, "ALC661" }, - { 0x10ec0892, 0xffff, 0x8011, "ALC661" }, - { 0x10ec0892, 0xffff, 0x4011, "ALC656" }, - { } /* terminator */ -}; - -static int alc_codec_rename_from_preset(struct hda_codec *codec) -{ - const struct alc_codec_rename_table *p; - - for (p = rename_tbl; p->vendor_id; p++) { - if (p->vendor_id != codec->vendor_id) - continue; - if ((alc_get_coef0(codec) & p->coef_mask) == p->coef_bits) - return alc_codec_rename(codec, p->name); - } - return 0; -} - -/* - * Automatic parse of I/O pins from the BIOS configuration - */ - -enum { - ALC_CTL_WIDGET_VOL, - ALC_CTL_WIDGET_MUTE, - ALC_CTL_BIND_MUTE, - ALC_CTL_BIND_VOL, - ALC_CTL_BIND_SW, -}; -static const struct snd_kcontrol_new alc_control_templates[] = { - HDA_CODEC_VOLUME(NULL, 0, 0, 0), - HDA_CODEC_MUTE(NULL, 0, 0, 0), - HDA_BIND_MUTE(NULL, 0, 0, 0), - HDA_BIND_VOL(NULL, 0), - HDA_BIND_SW(NULL, 0), -}; - -/* add dynamic controls */ -static int add_control(struct alc_spec *spec, int type, const char *name, - int cidx, unsigned long val) -{ - struct snd_kcontrol_new *knew; - - knew = alc_kcontrol_new(spec); - if (!knew) - return -ENOMEM; - *knew = alc_control_templates[type]; - knew->name = kstrdup(name, GFP_KERNEL); - if (!knew->name) - return -ENOMEM; - knew->index = cidx; - if (get_amp_nid_(val)) - knew->subdevice = HDA_SUBDEV_AMP_FLAG; - knew->private_value = val; - return 0; -} - -static int add_control_with_pfx(struct alc_spec *spec, int type, - const char *pfx, const char *dir, - const char *sfx, int cidx, unsigned long val) -{ - char name[32]; - snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx); - return add_control(spec, type, name, cidx, val); -} - -#define add_pb_vol_ctrl(spec, type, pfx, val) \ - add_control_with_pfx(spec, type, pfx, "Playback", "Volume", 0, val) -#define add_pb_sw_ctrl(spec, type, pfx, val) \ - add_control_with_pfx(spec, type, pfx, "Playback", "Switch", 0, val) -#define __add_pb_vol_ctrl(spec, type, pfx, cidx, val) \ - add_control_with_pfx(spec, type, pfx, "Playback", "Volume", cidx, val) -#define __add_pb_sw_ctrl(spec, type, pfx, cidx, val) \ - add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val) - -static const char * const channel_name[4] = { - "Front", "Surround", "CLFE", "Side" -}; - -static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch, - bool can_be_master, int *index) -{ - struct auto_pin_cfg *cfg = &spec->autocfg; - - *index = 0; - if (cfg->line_outs == 1 && !spec->multi_ios && - !cfg->hp_outs && !cfg->speaker_outs && can_be_master) - return "Master"; - - switch (cfg->line_out_type) { - case AUTO_PIN_SPEAKER_OUT: - if (cfg->line_outs == 1) - return "Speaker"; - if (cfg->line_outs == 2) - return ch ? "Bass Speaker" : "Speaker"; - break; - case AUTO_PIN_HP_OUT: - /* for multi-io case, only the primary out */ - if (ch && spec->multi_ios) - break; - *index = ch; - return "Headphone"; - default: - if (cfg->line_outs == 1 && !spec->multi_ios) - return "PCM"; - break; - } - if (snd_BUG_ON(ch >= ARRAY_SIZE(channel_name))) - return "PCM"; - - return channel_name[ch]; -} - -#ifdef CONFIG_SND_HDA_POWER_SAVE -/* add the powersave loopback-list entry */ -static void add_loopback_list(struct alc_spec *spec, hda_nid_t mix, int idx) -{ - struct hda_amp_list *list; - - if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1) - return; - list = spec->loopback_list + spec->num_loopbacks; - list->nid = mix; - list->dir = HDA_INPUT; - list->idx = idx; - spec->num_loopbacks++; - spec->loopback.amplist = spec->loopback_list; -} -#else -#define add_loopback_list(spec, mix, idx) /* NOP */ -#endif - -/* create input playback/capture controls for the given pin */ -static int new_analog_input(struct alc_spec *spec, hda_nid_t pin, - const char *ctlname, int ctlidx, - int idx, hda_nid_t mix_nid) -{ - int err; - - err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, ctlidx, - HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); - if (err < 0) - return err; - err = __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, ctlidx, - HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); - if (err < 0) - return err; - add_loopback_list(spec, mix_nid, idx); - return 0; -} - -static int alc_is_input_pin(struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int pincap = snd_hda_query_pin_caps(codec, nid); - return (pincap & AC_PINCAP_IN) != 0; -} - -/* Parse the codec tree and retrieve ADCs and corresponding capsrc MUXs */ -static int alc_auto_fill_adc_caps(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t nid; - hda_nid_t *adc_nids = spec->private_adc_nids; - hda_nid_t *cap_nids = spec->private_capsrc_nids; - int max_nums = ARRAY_SIZE(spec->private_adc_nids); - int i, nums = 0; - - nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, nid++) { - hda_nid_t src; - const hda_nid_t *list; - unsigned int caps = get_wcaps(codec, nid); - int type = get_wcaps_type(caps); - - if (type != AC_WID_AUD_IN || (caps & AC_WCAP_DIGITAL)) - continue; - adc_nids[nums] = nid; - cap_nids[nums] = nid; - src = nid; - for (;;) { - int n; - type = get_wcaps_type(get_wcaps(codec, src)); - if (type == AC_WID_PIN) - break; - if (type == AC_WID_AUD_SEL) { - cap_nids[nums] = src; - break; - } - n = snd_hda_get_conn_list(codec, src, &list); - if (n > 1) { - cap_nids[nums] = src; - break; - } else if (n != 1) - break; - src = *list; - } - if (++nums >= max_nums) - break; - } - spec->adc_nids = spec->private_adc_nids; - spec->capsrc_nids = spec->private_capsrc_nids; - spec->num_adc_nids = nums; - return nums; -} - -/* create playback/capture controls for input pins */ -static int alc_auto_create_input_ctls(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t mixer = spec->mixer_nid; - struct hda_input_mux *imux = &spec->private_imux[0]; - int num_adcs; - int i, c, err, idx, type_idx = 0; - const char *prev_label = NULL; - - num_adcs = alc_auto_fill_adc_caps(codec); - if (num_adcs < 0) - return 0; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t pin; - const char *label; - - pin = cfg->inputs[i].pin; - if (!alc_is_input_pin(codec, pin)) - continue; - - label = hda_get_autocfg_input_label(codec, cfg, i); - if (spec->shared_mic_hp && !strcmp(label, "Misc")) - label = "Headphone Mic"; - if (prev_label && !strcmp(label, prev_label)) - type_idx++; - else - type_idx = 0; - prev_label = label; - - if (mixer) { - idx = get_connection_index(codec, mixer, pin); - if (idx >= 0) { - err = new_analog_input(spec, pin, - label, type_idx, - idx, mixer); - if (err < 0) - return err; - } - } - - for (c = 0; c < num_adcs; c++) { - hda_nid_t cap = get_capsrc(spec, c); - idx = get_connection_index(codec, cap, pin); - if (idx >= 0) { - spec->imux_pins[imux->num_items] = pin; - snd_hda_add_imux_item(imux, label, idx, NULL); - break; - } - } - } - - spec->num_mux_defs = 1; - spec->input_mux = imux; - - return 0; -} - -/* create a shared input with the headphone out */ -static int alc_auto_create_shared_input(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int defcfg; - hda_nid_t nid; - - /* only one internal input pin? */ - if (cfg->num_inputs != 1) - return 0; - defcfg = snd_hda_codec_get_pincfg(codec, cfg->inputs[0].pin); - if (snd_hda_get_input_pin_attr(defcfg) != INPUT_PIN_ATTR_INT) - return 0; - - if (cfg->hp_outs == 1 && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) - nid = cfg->hp_pins[0]; /* OK, we have a single HP-out */ - else if (cfg->line_outs == 1 && cfg->line_out_type == AUTO_PIN_HP_OUT) - nid = cfg->line_out_pins[0]; /* OK, we have a single line-out */ - else - return 0; /* both not available */ - - if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_IN)) - return 0; /* no input */ - - cfg->inputs[1].pin = nid; - cfg->inputs[1].type = AUTO_PIN_MIC; - cfg->num_inputs = 2; - spec->shared_mic_hp = 1; - snd_printdd("realtek: Enable shared I/O jack on NID 0x%x\n", nid); - return 0; -} - -static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid, - unsigned int pin_type) -{ - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pin_type); - /* unmute pin */ - if (nid_has_mute(codec, nid, HDA_OUTPUT)) - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); -} - -static int get_pin_type(int line_out_type) -{ - if (line_out_type == AUTO_PIN_HP_OUT) - return PIN_HP; - else - return PIN_OUT; -} - -static void alc_auto_init_analog_input(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - if (alc_is_input_pin(codec, nid)) { - alc_set_input_pin(codec, nid, cfg->inputs[i].type); - if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_MUTE); - } - } - - /* mute all loopback inputs */ - if (spec->mixer_nid) { - int nums = snd_hda_get_conn_list(codec, spec->mixer_nid, NULL); - for (i = 0; i < nums; i++) - snd_hda_codec_write(codec, spec->mixer_nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_MUTE(i)); - } -} - -/* convert from MIX nid to DAC */ -static hda_nid_t alc_auto_mix_to_dac(struct hda_codec *codec, hda_nid_t nid) -{ - hda_nid_t list[5]; - int i, num; - - if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_AUD_OUT) - return nid; - num = snd_hda_get_connections(codec, nid, list, ARRAY_SIZE(list)); - for (i = 0; i < num; i++) { - if (get_wcaps_type(get_wcaps(codec, list[i])) == AC_WID_AUD_OUT) - return list[i]; - } - return 0; -} - -/* go down to the selector widget before the mixer */ -static hda_nid_t alc_go_down_to_selector(struct hda_codec *codec, hda_nid_t pin) -{ - hda_nid_t srcs[5]; - int num = snd_hda_get_connections(codec, pin, srcs, - ARRAY_SIZE(srcs)); - if (num != 1 || - get_wcaps_type(get_wcaps(codec, srcs[0])) != AC_WID_AUD_SEL) - return pin; - return srcs[0]; -} - -/* get MIX nid connected to the given pin targeted to DAC */ -static hda_nid_t alc_auto_dac_to_mix(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t dac) -{ - hda_nid_t mix[5]; - int i, num; - - pin = alc_go_down_to_selector(codec, pin); - num = snd_hda_get_connections(codec, pin, mix, ARRAY_SIZE(mix)); - for (i = 0; i < num; i++) { - if (alc_auto_mix_to_dac(codec, mix[i]) == dac) - return mix[i]; - } - return 0; -} - -/* select the connection from pin to DAC if needed */ -static int alc_auto_select_dac(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t dac) -{ - hda_nid_t mix[5]; - int i, num; - - pin = alc_go_down_to_selector(codec, pin); - num = snd_hda_get_connections(codec, pin, mix, ARRAY_SIZE(mix)); - if (num < 2) - return 0; - for (i = 0; i < num; i++) { - if (alc_auto_mix_to_dac(codec, mix[i]) == dac) { - snd_hda_codec_update_cache(codec, pin, 0, - AC_VERB_SET_CONNECT_SEL, i); - return 0; - } - } - return 0; -} - -static bool alc_is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) -{ - struct alc_spec *spec = codec->spec; - int i; - if (found_in_nid_list(nid, spec->multiout.dac_nids, - ARRAY_SIZE(spec->private_dac_nids)) || - found_in_nid_list(nid, spec->multiout.hp_out_nid, - ARRAY_SIZE(spec->multiout.hp_out_nid)) || - found_in_nid_list(nid, spec->multiout.extra_out_nid, - ARRAY_SIZE(spec->multiout.extra_out_nid))) - return true; - for (i = 0; i < spec->multi_ios; i++) { - if (spec->multi_io[i].dac == nid) - return true; - } - return false; -} - -/* look for an empty DAC slot */ -static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin) -{ - hda_nid_t srcs[5]; - int i, num; - - pin = alc_go_down_to_selector(codec, pin); - num = snd_hda_get_connections(codec, pin, srcs, ARRAY_SIZE(srcs)); - for (i = 0; i < num; i++) { - hda_nid_t nid = alc_auto_mix_to_dac(codec, srcs[i]); - if (!nid) - continue; - if (!alc_is_dac_already_used(codec, nid)) - return nid; - } - return 0; -} - -/* check whether the DAC is reachable from the pin */ -static bool alc_auto_is_dac_reachable(struct hda_codec *codec, - hda_nid_t pin, hda_nid_t dac) -{ - hda_nid_t srcs[5]; - int i, num; - - if (!pin || !dac) - return false; - pin = alc_go_down_to_selector(codec, pin); - num = snd_hda_get_connections(codec, pin, srcs, ARRAY_SIZE(srcs)); - for (i = 0; i < num; i++) { - hda_nid_t nid = alc_auto_mix_to_dac(codec, srcs[i]); - if (nid == dac) - return true; - } - return false; -} - -static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t sel = alc_go_down_to_selector(codec, pin); - hda_nid_t nid, nid_found, srcs[5]; - int i, num = snd_hda_get_connections(codec, sel, srcs, - ARRAY_SIZE(srcs)); - if (num == 1) - return alc_auto_look_for_dac(codec, pin); - nid_found = 0; - for (i = 0; i < num; i++) { - if (srcs[i] == spec->mixer_nid) - continue; - nid = alc_auto_mix_to_dac(codec, srcs[i]); - if (nid && !alc_is_dac_already_used(codec, nid)) { - if (nid_found) - return 0; - nid_found = nid; - } - } - return nid_found; -} - -/* mark up volume and mute control NIDs: used during badness parsing and - * at creating actual controls - */ -static inline unsigned int get_ctl_pos(unsigned int data) -{ - hda_nid_t nid = get_amp_nid_(data); - unsigned int dir; - if (snd_BUG_ON(nid >= MAX_VOL_NIDS)) - return 0; - dir = get_amp_direction_(data); - return (nid << 1) | dir; -} - -#define is_ctl_used(bits, data) \ - test_bit(get_ctl_pos(data), bits) -#define mark_ctl_usage(bits, data) \ - set_bit(get_ctl_pos(data), bits) - -static void clear_vol_marks(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - memset(spec->vol_ctls, 0, sizeof(spec->vol_ctls)); - memset(spec->sw_ctls, 0, sizeof(spec->sw_ctls)); -} - -/* badness definition */ -enum { - /* No primary DAC is found for the main output */ - BAD_NO_PRIMARY_DAC = 0x10000, - /* No DAC is found for the extra output */ - BAD_NO_DAC = 0x4000, - /* No possible multi-ios */ - BAD_MULTI_IO = 0x103, - /* No individual DAC for extra output */ - BAD_NO_EXTRA_DAC = 0x102, - /* No individual DAC for extra surrounds */ - BAD_NO_EXTRA_SURR_DAC = 0x101, - /* Primary DAC shared with main surrounds */ - BAD_SHARED_SURROUND = 0x100, - /* Primary DAC shared with main CLFE */ - BAD_SHARED_CLFE = 0x10, - /* Primary DAC shared with extra surrounds */ - BAD_SHARED_EXTRA_SURROUND = 0x10, - /* Volume widget is shared */ - BAD_SHARED_VOL = 0x10, -}; - -static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec, - hda_nid_t pin, hda_nid_t dac); -static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec, - hda_nid_t pin, hda_nid_t dac); - -static int eval_shared_vol_badness(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t dac) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t nid; - unsigned int val; - int badness = 0; - - nid = alc_look_for_out_vol_nid(codec, pin, dac); - if (nid) { - val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - if (is_ctl_used(spec->vol_ctls, nid)) - badness += BAD_SHARED_VOL; - else - mark_ctl_usage(spec->vol_ctls, val); - } else - badness += BAD_SHARED_VOL; - nid = alc_look_for_out_mute_nid(codec, pin, dac); - if (nid) { - unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid)); - if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT) - val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - else - val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT); - if (is_ctl_used(spec->sw_ctls, val)) - badness += BAD_SHARED_VOL; - else - mark_ctl_usage(spec->sw_ctls, val); - } else - badness += BAD_SHARED_VOL; - return badness; -} - -struct badness_table { - int no_primary_dac; /* no primary DAC */ - int no_dac; /* no secondary DACs */ - int shared_primary; /* primary DAC is shared with main output */ - int shared_surr; /* secondary DAC shared with main or primary */ - int shared_clfe; /* third DAC shared with main or primary */ - int shared_surr_main; /* secondary DAC sahred with main/DAC0 */ -}; - -static struct badness_table main_out_badness = { - .no_primary_dac = BAD_NO_PRIMARY_DAC, - .no_dac = BAD_NO_DAC, - .shared_primary = BAD_NO_PRIMARY_DAC, - .shared_surr = BAD_SHARED_SURROUND, - .shared_clfe = BAD_SHARED_CLFE, - .shared_surr_main = BAD_SHARED_SURROUND, -}; - -static struct badness_table extra_out_badness = { - .no_primary_dac = BAD_NO_DAC, - .no_dac = BAD_NO_DAC, - .shared_primary = BAD_NO_EXTRA_DAC, - .shared_surr = BAD_SHARED_EXTRA_SURROUND, - .shared_clfe = BAD_SHARED_EXTRA_SURROUND, - .shared_surr_main = BAD_NO_EXTRA_SURR_DAC, -}; - -/* try to assign DACs to pins and return the resultant badness */ -static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs, - const hda_nid_t *pins, hda_nid_t *dacs, - const struct badness_table *bad) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i, j; - int badness = 0; - hda_nid_t dac; - - if (!num_outs) - return 0; - - for (i = 0; i < num_outs; i++) { - hda_nid_t pin = pins[i]; - if (!dacs[i]) - dacs[i] = alc_auto_look_for_dac(codec, pin); - if (!dacs[i] && !i) { - for (j = 1; j < num_outs; j++) { - if (alc_auto_is_dac_reachable(codec, pin, dacs[j])) { - dacs[0] = dacs[j]; - dacs[j] = 0; - break; - } - } - } - dac = dacs[i]; - if (!dac) { - if (alc_auto_is_dac_reachable(codec, pin, dacs[0])) - dac = dacs[0]; - else if (cfg->line_outs > i && - alc_auto_is_dac_reachable(codec, pin, - spec->private_dac_nids[i])) - dac = spec->private_dac_nids[i]; - if (dac) { - if (!i) - badness += bad->shared_primary; - else if (i == 1) - badness += bad->shared_surr; - else - badness += bad->shared_clfe; - } else if (alc_auto_is_dac_reachable(codec, pin, - spec->private_dac_nids[0])) { - dac = spec->private_dac_nids[0]; - badness += bad->shared_surr_main; - } else if (!i) - badness += bad->no_primary_dac; - else - badness += bad->no_dac; - } - if (dac) - badness += eval_shared_vol_badness(codec, pin, dac); - } - - return badness; -} - -static int alc_auto_fill_multi_ios(struct hda_codec *codec, - hda_nid_t reference_pin, - bool hardwired, int offset); - -static bool alc_map_singles(struct hda_codec *codec, int outs, - const hda_nid_t *pins, hda_nid_t *dacs) -{ - int i; - bool found = false; - for (i = 0; i < outs; i++) { - if (dacs[i]) - continue; - dacs[i] = get_dac_if_single(codec, pins[i]); - if (dacs[i]) - found = true; - } - return found; -} - -/* fill in the dac_nids table from the parsed pin configuration */ -static int fill_and_eval_dacs(struct hda_codec *codec, - bool fill_hardwired, - bool fill_mio_first) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i, err, badness; - - /* set num_dacs once to full for alc_auto_look_for_dac() */ - spec->multiout.num_dacs = cfg->line_outs; - spec->multiout.dac_nids = spec->private_dac_nids; - memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids)); - memset(spec->multiout.hp_out_nid, 0, sizeof(spec->multiout.hp_out_nid)); - memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid)); - spec->multi_ios = 0; - clear_vol_marks(codec); - badness = 0; - - /* fill hard-wired DACs first */ - if (fill_hardwired) { - bool mapped; - do { - mapped = alc_map_singles(codec, cfg->line_outs, - cfg->line_out_pins, - spec->private_dac_nids); - mapped |= alc_map_singles(codec, cfg->hp_outs, - cfg->hp_pins, - spec->multiout.hp_out_nid); - mapped |= alc_map_singles(codec, cfg->speaker_outs, - cfg->speaker_pins, - spec->multiout.extra_out_nid); - if (fill_mio_first && cfg->line_outs == 1 && - cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - err = alc_auto_fill_multi_ios(codec, cfg->line_out_pins[0], true, 0); - if (!err) - mapped = true; - } - } while (mapped); - } - - badness += alc_auto_fill_dacs(codec, cfg->line_outs, cfg->line_out_pins, - spec->private_dac_nids, - &main_out_badness); - - /* re-count num_dacs and squash invalid entries */ - spec->multiout.num_dacs = 0; - for (i = 0; i < cfg->line_outs; i++) { - if (spec->private_dac_nids[i]) - spec->multiout.num_dacs++; - else { - memmove(spec->private_dac_nids + i, - spec->private_dac_nids + i + 1, - sizeof(hda_nid_t) * (cfg->line_outs - i - 1)); - spec->private_dac_nids[cfg->line_outs - 1] = 0; - } - } - - if (fill_mio_first && - cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - /* try to fill multi-io first */ - err = alc_auto_fill_multi_ios(codec, cfg->line_out_pins[0], false, 0); - if (err < 0) - return err; - /* we don't count badness at this stage yet */ - } - - if (cfg->line_out_type != AUTO_PIN_HP_OUT) { - err = alc_auto_fill_dacs(codec, cfg->hp_outs, cfg->hp_pins, - spec->multiout.hp_out_nid, - &extra_out_badness); - if (err < 0) - return err; - badness += err; - } - if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - err = alc_auto_fill_dacs(codec, cfg->speaker_outs, - cfg->speaker_pins, - spec->multiout.extra_out_nid, - &extra_out_badness); - if (err < 0) - return err; - badness += err; - } - if (cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - err = alc_auto_fill_multi_ios(codec, cfg->line_out_pins[0], false, 0); - if (err < 0) - return err; - badness += err; - } - if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { - /* try multi-ios with HP + inputs */ - int offset = 0; - if (cfg->line_outs >= 3) - offset = 1; - err = alc_auto_fill_multi_ios(codec, cfg->hp_pins[0], false, - offset); - if (err < 0) - return err; - badness += err; - } - - if (spec->multi_ios == 2) { - for (i = 0; i < 2; i++) - spec->private_dac_nids[spec->multiout.num_dacs++] = - spec->multi_io[i].dac; - spec->ext_channel_count = 2; - } else if (spec->multi_ios) { - spec->multi_ios = 0; - badness += BAD_MULTI_IO; - } - - return badness; -} - -#define DEBUG_BADNESS - -#ifdef DEBUG_BADNESS -#define debug_badness snd_printdd -#else -#define debug_badness(...) -#endif - -static void debug_show_configs(struct alc_spec *spec, struct auto_pin_cfg *cfg) -{ - debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", - cfg->line_out_pins[0], cfg->line_out_pins[1], - cfg->line_out_pins[2], cfg->line_out_pins[2], - spec->multiout.dac_nids[0], - spec->multiout.dac_nids[1], - spec->multiout.dac_nids[2], - spec->multiout.dac_nids[3]); - if (spec->multi_ios > 0) - debug_badness("multi_ios(%d) = %x/%x : %x/%x\n", - spec->multi_ios, - spec->multi_io[0].pin, spec->multi_io[1].pin, - spec->multi_io[0].dac, spec->multi_io[1].dac); - debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", - cfg->hp_pins[0], cfg->hp_pins[1], - cfg->hp_pins[2], cfg->hp_pins[2], - spec->multiout.hp_out_nid[0], - spec->multiout.hp_out_nid[1], - spec->multiout.hp_out_nid[2], - spec->multiout.hp_out_nid[3]); - debug_badness("spk_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", - cfg->speaker_pins[0], cfg->speaker_pins[1], - cfg->speaker_pins[2], cfg->speaker_pins[3], - spec->multiout.extra_out_nid[0], - spec->multiout.extra_out_nid[1], - spec->multiout.extra_out_nid[2], - spec->multiout.extra_out_nid[3]); -} - -static int alc_auto_fill_dac_nids(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - struct auto_pin_cfg *best_cfg; - int best_badness = INT_MAX; - int badness; - bool fill_hardwired = true, fill_mio_first = true; - bool best_wired = true, best_mio = true; - bool hp_spk_swapped = false; - - best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL); - if (!best_cfg) - return -ENOMEM; - *best_cfg = *cfg; - - for (;;) { - badness = fill_and_eval_dacs(codec, fill_hardwired, - fill_mio_first); - if (badness < 0) { - kfree(best_cfg); - return badness; - } - debug_badness("==> lo_type=%d, wired=%d, mio=%d, badness=0x%x\n", - cfg->line_out_type, fill_hardwired, fill_mio_first, - badness); - debug_show_configs(spec, cfg); - if (badness < best_badness) { - best_badness = badness; - *best_cfg = *cfg; - best_wired = fill_hardwired; - best_mio = fill_mio_first; - } - if (!badness) - break; - fill_mio_first = !fill_mio_first; - if (!fill_mio_first) - continue; - fill_hardwired = !fill_hardwired; - if (!fill_hardwired) - continue; - if (hp_spk_swapped) - break; - hp_spk_swapped = true; - if (cfg->speaker_outs > 0 && - cfg->line_out_type == AUTO_PIN_HP_OUT) { - cfg->hp_outs = cfg->line_outs; - memcpy(cfg->hp_pins, cfg->line_out_pins, - sizeof(cfg->hp_pins)); - cfg->line_outs = cfg->speaker_outs; - memcpy(cfg->line_out_pins, cfg->speaker_pins, - sizeof(cfg->speaker_pins)); - cfg->speaker_outs = 0; - memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins)); - cfg->line_out_type = AUTO_PIN_SPEAKER_OUT; - fill_hardwired = true; - continue; - } - if (cfg->hp_outs > 0 && - cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { - cfg->speaker_outs = cfg->line_outs; - memcpy(cfg->speaker_pins, cfg->line_out_pins, - sizeof(cfg->speaker_pins)); - cfg->line_outs = cfg->hp_outs; - memcpy(cfg->line_out_pins, cfg->hp_pins, - sizeof(cfg->hp_pins)); - cfg->hp_outs = 0; - memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); - cfg->line_out_type = AUTO_PIN_HP_OUT; - fill_hardwired = true; - continue; - } - break; - } - - if (badness) { - *cfg = *best_cfg; - fill_and_eval_dacs(codec, best_wired, best_mio); - } - debug_badness("==> Best config: lo_type=%d, wired=%d, mio=%d\n", - cfg->line_out_type, best_wired, best_mio); - debug_show_configs(spec, cfg); - - if (cfg->line_out_pins[0]) - spec->vmaster_nid = - alc_look_for_out_vol_nid(codec, cfg->line_out_pins[0], - spec->multiout.dac_nids[0]); - - /* clear the bitmap flags for creating controls */ - clear_vol_marks(codec); - kfree(best_cfg); - return 0; -} - -static int alc_auto_add_vol_ctl(struct hda_codec *codec, - const char *pfx, int cidx, - hda_nid_t nid, unsigned int chs) -{ - struct alc_spec *spec = codec->spec; - unsigned int val; - if (!nid) - return 0; - val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT); - if (is_ctl_used(spec->vol_ctls, val) && chs != 2) /* exclude LFE */ - return 0; - mark_ctl_usage(spec->vol_ctls, val); - return __add_pb_vol_ctrl(codec->spec, ALC_CTL_WIDGET_VOL, pfx, cidx, - val); -} - -static int alc_auto_add_stereo_vol(struct hda_codec *codec, - const char *pfx, int cidx, - hda_nid_t nid) -{ - int chs = 1; - if (get_wcaps(codec, nid) & AC_WCAP_STEREO) - chs = 3; - return alc_auto_add_vol_ctl(codec, pfx, cidx, nid, chs); -} - -/* create a mute-switch for the given mixer widget; - * if it has multiple sources (e.g. DAC and loopback), create a bind-mute - */ -static int alc_auto_add_sw_ctl(struct hda_codec *codec, - const char *pfx, int cidx, - hda_nid_t nid, unsigned int chs) -{ - struct alc_spec *spec = codec->spec; - int wid_type; - int type; - unsigned long val; - if (!nid) - return 0; - wid_type = get_wcaps_type(get_wcaps(codec, nid)); - if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT) { - type = ALC_CTL_WIDGET_MUTE; - val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT); - } else if (snd_hda_get_conn_list(codec, nid, NULL) == 1) { - type = ALC_CTL_WIDGET_MUTE; - val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT); - } else { - type = ALC_CTL_BIND_MUTE; - val = HDA_COMPOSE_AMP_VAL(nid, chs, 2, HDA_INPUT); - } - if (is_ctl_used(spec->sw_ctls, val) && chs != 2) /* exclude LFE */ - return 0; - mark_ctl_usage(spec->sw_ctls, val); - return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val); -} - -static int alc_auto_add_stereo_sw(struct hda_codec *codec, const char *pfx, - int cidx, hda_nid_t nid) -{ - int chs = 1; - if (get_wcaps(codec, nid) & AC_WCAP_STEREO) - chs = 3; - return alc_auto_add_sw_ctl(codec, pfx, cidx, nid, chs); -} - -static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec, - hda_nid_t pin, hda_nid_t dac) -{ - hda_nid_t mix = alc_auto_dac_to_mix(codec, pin, dac); - if (nid_has_mute(codec, pin, HDA_OUTPUT)) - return pin; - else if (mix && nid_has_mute(codec, mix, HDA_INPUT)) - return mix; - else if (nid_has_mute(codec, dac, HDA_OUTPUT)) - return dac; - return 0; -} - -static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec, - hda_nid_t pin, hda_nid_t dac) -{ - hda_nid_t mix = alc_auto_dac_to_mix(codec, pin, dac); - if (nid_has_volume(codec, dac, HDA_OUTPUT)) - return dac; - else if (nid_has_volume(codec, mix, HDA_OUTPUT)) - return mix; - else if (nid_has_volume(codec, pin, HDA_OUTPUT)) - return pin; - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int alc_auto_create_multi_out_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - struct alc_spec *spec = codec->spec; - int i, err, noutputs; - - noutputs = cfg->line_outs; - if (spec->multi_ios > 0 && cfg->line_outs < 3) - noutputs += spec->multi_ios; - - for (i = 0; i < noutputs; i++) { - const char *name; - int index; - hda_nid_t dac, pin; - hda_nid_t sw, vol; - - dac = spec->multiout.dac_nids[i]; - if (!dac) - continue; - if (i >= cfg->line_outs) { - pin = spec->multi_io[i - 1].pin; - index = 0; - name = channel_name[i]; - } else { - pin = cfg->line_out_pins[i]; - name = alc_get_line_out_pfx(spec, i, true, &index); - } - - sw = alc_look_for_out_mute_nid(codec, pin, dac); - vol = alc_look_for_out_vol_nid(codec, pin, dac); - if (!name || !strcmp(name, "CLFE")) { - /* Center/LFE */ - err = alc_auto_add_vol_ctl(codec, "Center", 0, vol, 1); - if (err < 0) - return err; - err = alc_auto_add_vol_ctl(codec, "LFE", 0, vol, 2); - if (err < 0) - return err; - err = alc_auto_add_sw_ctl(codec, "Center", 0, sw, 1); - if (err < 0) - return err; - err = alc_auto_add_sw_ctl(codec, "LFE", 0, sw, 2); - if (err < 0) - return err; - } else { - err = alc_auto_add_stereo_vol(codec, name, index, vol); - if (err < 0) - return err; - err = alc_auto_add_stereo_sw(codec, name, index, sw); - if (err < 0) - return err; - } - } - return 0; -} - -static int alc_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t dac, const char *pfx, - int cidx) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t sw, vol; - int err; - - if (!dac) { - unsigned int val; - /* the corresponding DAC is already occupied */ - if (!(get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)) - return 0; /* no way */ - /* create a switch only */ - val = HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT); - if (is_ctl_used(spec->sw_ctls, val)) - return 0; /* already created */ - mark_ctl_usage(spec->sw_ctls, val); - return __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, cidx, val); - } - - sw = alc_look_for_out_mute_nid(codec, pin, dac); - vol = alc_look_for_out_vol_nid(codec, pin, dac); - err = alc_auto_add_stereo_vol(codec, pfx, cidx, vol); - if (err < 0) - return err; - err = alc_auto_add_stereo_sw(codec, pfx, cidx, sw); - if (err < 0) - return err; - return 0; -} - -static struct hda_bind_ctls *new_bind_ctl(struct hda_codec *codec, - unsigned int nums, - struct hda_ctl_ops *ops) -{ - struct alc_spec *spec = codec->spec; - struct hda_bind_ctls **ctlp, *ctl; - snd_array_init(&spec->bind_ctls, sizeof(ctl), 8); - ctlp = snd_array_new(&spec->bind_ctls); - if (!ctlp) - return NULL; - ctl = kzalloc(sizeof(*ctl) + sizeof(long) * (nums + 1), GFP_KERNEL); - *ctlp = ctl; - if (ctl) - ctl->ops = ops; - return ctl; -} - -/* add playback controls for speaker and HP outputs */ -static int alc_auto_create_extra_outs(struct hda_codec *codec, int num_pins, - const hda_nid_t *pins, - const hda_nid_t *dacs, - const char *pfx) -{ - struct alc_spec *spec = codec->spec; - struct hda_bind_ctls *ctl; - char name[32]; - int i, n, err; - - if (!num_pins || !pins[0]) - return 0; - - if (num_pins == 1) { - hda_nid_t dac = *dacs; - if (!dac) - dac = spec->multiout.dac_nids[0]; - return alc_auto_create_extra_out(codec, *pins, dac, pfx, 0); - } - - for (i = 0; i < num_pins; i++) { - hda_nid_t dac; - if (dacs[num_pins - 1]) - dac = dacs[i]; /* with individual volumes */ - else - dac = 0; - if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker")) { - err = alc_auto_create_extra_out(codec, pins[i], dac, - "Bass Speaker", 0); - } else if (num_pins >= 3) { - snprintf(name, sizeof(name), "%s %s", - pfx, channel_name[i]); - err = alc_auto_create_extra_out(codec, pins[i], dac, - name, 0); - } else { - err = alc_auto_create_extra_out(codec, pins[i], dac, - pfx, i); - } - if (err < 0) - return err; - } - if (dacs[num_pins - 1]) - return 0; - - /* Let's create a bind-controls for volumes */ - ctl = new_bind_ctl(codec, num_pins, &snd_hda_bind_vol); - if (!ctl) - return -ENOMEM; - n = 0; - for (i = 0; i < num_pins; i++) { - hda_nid_t vol; - if (!pins[i] || !dacs[i]) - continue; - vol = alc_look_for_out_vol_nid(codec, pins[i], dacs[i]); - if (vol) - ctl->values[n++] = - HDA_COMPOSE_AMP_VAL(vol, 3, 0, HDA_OUTPUT); - } - if (n) { - snprintf(name, sizeof(name), "%s Playback Volume", pfx); - err = add_control(spec, ALC_CTL_BIND_VOL, name, 0, (long)ctl); - if (err < 0) - return err; - } - return 0; -} - -static int alc_auto_create_hp_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - return alc_auto_create_extra_outs(codec, spec->autocfg.hp_outs, - spec->autocfg.hp_pins, - spec->multiout.hp_out_nid, - "Headphone"); -} - -static int alc_auto_create_speaker_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - return alc_auto_create_extra_outs(codec, spec->autocfg.speaker_outs, - spec->autocfg.speaker_pins, - spec->multiout.extra_out_nid, - "Speaker"); -} - -static void alc_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t pin, int pin_type, - hda_nid_t dac) -{ - int i, num; - hda_nid_t nid, mix = 0; - hda_nid_t srcs[HDA_MAX_CONNECTIONS]; - - alc_set_pin_output(codec, pin, pin_type); - nid = alc_go_down_to_selector(codec, pin); - num = snd_hda_get_connections(codec, nid, srcs, ARRAY_SIZE(srcs)); - for (i = 0; i < num; i++) { - if (alc_auto_mix_to_dac(codec, srcs[i]) != dac) - continue; - mix = srcs[i]; - break; - } - if (!mix) - return; - - /* need the manual connection? */ - if (num > 1) - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, i); - /* unmute mixer widget inputs */ - if (nid_has_mute(codec, mix, HDA_INPUT)) { - snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(0)); - snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(1)); - } - /* initialize volume */ - nid = alc_look_for_out_vol_nid(codec, pin, dac); - if (nid) - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_ZERO); - - /* unmute DAC if it's not assigned to a mixer */ - nid = alc_look_for_out_mute_nid(codec, pin, dac); - if (nid == mix && nid_has_mute(codec, dac, HDA_OUTPUT)) - snd_hda_codec_write(codec, dac, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_ZERO); -} - -static void alc_auto_init_multi_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int pin_type = get_pin_type(spec->autocfg.line_out_type); - int i; - - for (i = 0; i <= HDA_SIDE; i++) { - hda_nid_t nid = spec->autocfg.line_out_pins[i]; - if (nid) - alc_auto_set_output_and_unmute(codec, nid, pin_type, - spec->multiout.dac_nids[i]); - } -} - -static void alc_auto_init_extra_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int i; - hda_nid_t pin, dac; - - for (i = 0; i < spec->autocfg.hp_outs; i++) { - if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) - break; - pin = spec->autocfg.hp_pins[i]; - if (!pin) - break; - dac = spec->multiout.hp_out_nid[i]; - if (!dac) { - if (i > 0 && spec->multiout.hp_out_nid[0]) - dac = spec->multiout.hp_out_nid[0]; - else - dac = spec->multiout.dac_nids[0]; - } - alc_auto_set_output_and_unmute(codec, pin, PIN_HP, dac); - } - for (i = 0; i < spec->autocfg.speaker_outs; i++) { - if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) - break; - pin = spec->autocfg.speaker_pins[i]; - if (!pin) - break; - dac = spec->multiout.extra_out_nid[i]; - if (!dac) { - if (i > 0 && spec->multiout.extra_out_nid[0]) - dac = spec->multiout.extra_out_nid[0]; - else - dac = spec->multiout.dac_nids[0]; - } - alc_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac); - } -} - -/* check whether the given pin can be a multi-io pin */ -static bool can_be_multiio_pin(struct hda_codec *codec, - unsigned int location, hda_nid_t nid) -{ - unsigned int defcfg, caps; - - defcfg = snd_hda_codec_get_pincfg(codec, nid); - if (get_defcfg_connect(defcfg) != AC_JACK_PORT_COMPLEX) - return false; - if (location && get_defcfg_location(defcfg) != location) - return false; - caps = snd_hda_query_pin_caps(codec, nid); - if (!(caps & AC_PINCAP_OUT)) - return false; - return true; -} - -/* - * multi-io helper - * - * When hardwired is set, try to fill ony hardwired pins, and returns - * zero if any pins are filled, non-zero if nothing found. - * When hardwired is off, try to fill possible input pins, and returns - * the badness value. - */ -static int alc_auto_fill_multi_ios(struct hda_codec *codec, - hda_nid_t reference_pin, - bool hardwired, int offset) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int type, i, j, dacs, num_pins, old_pins; - unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin); - unsigned int location = get_defcfg_location(defcfg); - int badness = 0; - - old_pins = spec->multi_ios; - if (old_pins >= 2) - goto end_fill; - - num_pins = 0; - for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { - for (i = 0; i < cfg->num_inputs; i++) { - if (cfg->inputs[i].type != type) - continue; - if (can_be_multiio_pin(codec, location, - cfg->inputs[i].pin)) - num_pins++; - } - } - if (num_pins < 2) - goto end_fill; - - dacs = spec->multiout.num_dacs; - for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - hda_nid_t dac = 0; - - if (cfg->inputs[i].type != type) - continue; - if (!can_be_multiio_pin(codec, location, nid)) - continue; - for (j = 0; j < spec->multi_ios; j++) { - if (nid == spec->multi_io[j].pin) - break; - } - if (j < spec->multi_ios) - continue; - - if (offset && offset + spec->multi_ios < dacs) { - dac = spec->private_dac_nids[offset + spec->multi_ios]; - if (!alc_auto_is_dac_reachable(codec, nid, dac)) - dac = 0; - } - if (hardwired) - dac = get_dac_if_single(codec, nid); - else if (!dac) - dac = alc_auto_look_for_dac(codec, nid); - if (!dac) { - badness++; - continue; - } - spec->multi_io[spec->multi_ios].pin = nid; - spec->multi_io[spec->multi_ios].dac = dac; - spec->multi_ios++; - if (spec->multi_ios >= 2) - break; - } - } - end_fill: - if (badness) - badness = BAD_MULTI_IO; - if (old_pins == spec->multi_ios) { - if (hardwired) - return 1; /* nothing found */ - else - return badness; /* no badness if nothing found */ - } - if (!hardwired && spec->multi_ios < 2) { - spec->multi_ios = old_pins; - return badness; - } - - return 0; -} - -static int alc_auto_ch_mode_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; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = spec->multi_ios + 1; - if (uinfo->value.enumerated.item > spec->multi_ios) - uinfo->value.enumerated.item = spec->multi_ios; - sprintf(uinfo->value.enumerated.name, "%dch", - (uinfo->value.enumerated.item + 1) * 2); - return 0; -} - -static int alc_auto_ch_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - ucontrol->value.enumerated.item[0] = (spec->ext_channel_count - 1) / 2; - return 0; -} - -static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t nid = spec->multi_io[idx].pin; - - if (!spec->multi_io[idx].ctl_in) - spec->multi_io[idx].ctl_in = - snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - if (output) { - snd_hda_codec_update_cache(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - PIN_OUT); - if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) - snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, - HDA_AMP_MUTE, 0); - alc_auto_select_dac(codec, nid, spec->multi_io[idx].dac); - } else { - if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) - snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, - HDA_AMP_MUTE, HDA_AMP_MUTE); - snd_hda_codec_update_cache(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - spec->multi_io[idx].ctl_in); - } - return 0; -} - -static int alc_auto_ch_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - int i, ch; - - ch = ucontrol->value.enumerated.item[0]; - if (ch < 0 || ch > spec->multi_ios) - return -EINVAL; - if (ch == (spec->ext_channel_count - 1) / 2) - return 0; - spec->ext_channel_count = (ch + 1) * 2; - for (i = 0; i < spec->multi_ios; i++) - alc_set_multi_io(codec, i, i < ch); - spec->multiout.max_channels = spec->ext_channel_count; - if (spec->need_dac_fix && !spec->const_channel_count) - spec->multiout.num_dacs = spec->multiout.max_channels / 2; - return 1; -} - -static const struct snd_kcontrol_new alc_auto_channel_mode_enum = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = alc_auto_ch_mode_info, - .get = alc_auto_ch_mode_get, - .put = alc_auto_ch_mode_put, -}; - -static int alc_auto_add_multi_channel_mode(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (spec->multi_ios > 0) { - struct snd_kcontrol_new *knew; - - knew = alc_kcontrol_new(spec); - if (!knew) - return -ENOMEM; - *knew = alc_auto_channel_mode_enum; - knew->name = kstrdup("Channel Mode", GFP_KERNEL); - if (!knew->name) - return -ENOMEM; - } - return 0; -} - -/* filter out invalid adc_nids (and capsrc_nids) that don't give all - * active input pins - */ -static void alc_remove_invalid_adc_nids(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - const struct hda_input_mux *imux; - hda_nid_t adc_nids[ARRAY_SIZE(spec->private_adc_nids)]; - hda_nid_t capsrc_nids[ARRAY_SIZE(spec->private_adc_nids)]; - int i, n, nums; - - imux = spec->input_mux; - if (!imux) - return; - if (spec->dyn_adc_switch) - return; - - again: - nums = 0; - for (n = 0; n < spec->num_adc_nids; n++) { - hda_nid_t cap = spec->private_capsrc_nids[n]; - int num_conns = snd_hda_get_conn_list(codec, cap, NULL); - for (i = 0; i < imux->num_items; i++) { - hda_nid_t pin = spec->imux_pins[i]; - if (pin) { - if (get_connection_index(codec, cap, pin) < 0) - break; - } else if (num_conns <= imux->items[i].index) - break; - } - if (i >= imux->num_items) { - adc_nids[nums] = spec->private_adc_nids[n]; - capsrc_nids[nums++] = cap; - } - } - if (!nums) { - /* check whether ADC-switch is possible */ - if (!alc_check_dyn_adc_switch(codec)) { - if (spec->shared_mic_hp) { - spec->shared_mic_hp = 0; - spec->private_imux[0].num_items = 1; - goto again; - } - printk(KERN_WARNING "hda_codec: %s: no valid ADC found;" - " using fallback 0x%x\n", - codec->chip_name, spec->private_adc_nids[0]); - spec->num_adc_nids = 1; - spec->auto_mic = 0; - return; - } - } else if (nums != spec->num_adc_nids) { - memcpy(spec->private_adc_nids, adc_nids, - nums * sizeof(hda_nid_t)); - memcpy(spec->private_capsrc_nids, capsrc_nids, - nums * sizeof(hda_nid_t)); - spec->num_adc_nids = nums; - } - - if (spec->auto_mic) - alc_auto_mic_check_imux(codec); /* check auto-mic setups */ - else if (spec->input_mux->num_items == 1 || spec->shared_mic_hp) - spec->num_adc_nids = 1; /* reduce to a single ADC */ -} - -/* - * initialize ADC paths - */ -static void alc_auto_init_adc(struct hda_codec *codec, int adc_idx) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t nid; - - nid = spec->adc_nids[adc_idx]; - /* mute ADC */ - if (nid_has_mute(codec, nid, HDA_INPUT)) { - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_MUTE(0)); - return; - } - if (!spec->capsrc_nids) - return; - nid = spec->capsrc_nids[adc_idx]; - if (nid_has_mute(codec, nid, HDA_OUTPUT)) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_MUTE); -} - -static void alc_auto_init_input_src(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int c, nums; - - for (c = 0; c < spec->num_adc_nids; c++) - alc_auto_init_adc(codec, c); - if (spec->dyn_adc_switch) - nums = 1; - else - nums = spec->num_adc_nids; - for (c = 0; c < nums; c++) - alc_mux_select(codec, c, spec->cur_mux[c], true); -} - -/* add mic boosts if needed */ -static int alc_auto_add_mic_boost(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i, err; - int type_idx = 0; - hda_nid_t nid; - const char *prev_label = NULL; - - for (i = 0; i < cfg->num_inputs; i++) { - if (cfg->inputs[i].type > AUTO_PIN_MIC) - break; - nid = cfg->inputs[i].pin; - if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) { - const char *label; - char boost_label[32]; - - label = hda_get_autocfg_input_label(codec, cfg, i); - if (spec->shared_mic_hp && !strcmp(label, "Misc")) - label = "Headphone Mic"; - if (prev_label && !strcmp(label, prev_label)) - type_idx++; - else - type_idx = 0; - prev_label = label; - - snprintf(boost_label, sizeof(boost_label), - "%s Boost Volume", label); - err = add_control(spec, ALC_CTL_WIDGET_VOL, - boost_label, type_idx, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT)); - if (err < 0) - return err; - } - } - return 0; -} - -/* select or unmute the given capsrc route */ -static void select_or_unmute_capsrc(struct hda_codec *codec, hda_nid_t cap, - int idx) -{ - if (get_wcaps_type(get_wcaps(codec, cap)) == AC_WID_AUD_MIX) { - snd_hda_codec_amp_stereo(codec, cap, HDA_INPUT, idx, - HDA_AMP_MUTE, 0); - } else if (snd_hda_get_conn_list(codec, cap, NULL) > 1) { - snd_hda_codec_write_cache(codec, cap, 0, - AC_VERB_SET_CONNECT_SEL, idx); - } -} - -/* set the default connection to that pin */ -static int init_capsrc_for_pin(struct hda_codec *codec, hda_nid_t pin) -{ - struct alc_spec *spec = codec->spec; - int i; - - if (!pin) - return 0; - for (i = 0; i < spec->num_adc_nids; i++) { - hda_nid_t cap = get_capsrc(spec, i); - int idx; - - idx = get_connection_index(codec, cap, pin); - if (idx < 0) - continue; - select_or_unmute_capsrc(codec, cap, idx); - return i; /* return the found index */ - } - return -1; /* not found */ -} - -/* initialize some special cases for input sources */ -static void alc_init_special_input_src(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->autocfg.num_inputs; i++) - init_capsrc_for_pin(codec, spec->autocfg.inputs[i].pin); -} - -/* assign appropriate capture mixers */ -static void set_capture_mixer(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - static const struct snd_kcontrol_new *caps[2][3] = { - { alc_capture_mixer_nosrc1, - alc_capture_mixer_nosrc2, - alc_capture_mixer_nosrc3 }, - { alc_capture_mixer1, - alc_capture_mixer2, - alc_capture_mixer3 }, - }; - - /* check whether either of ADC or MUX has a volume control */ - if (!nid_has_volume(codec, spec->adc_nids[0], HDA_INPUT)) { - if (!spec->capsrc_nids) - return; /* no volume */ - if (!nid_has_volume(codec, spec->capsrc_nids[0], HDA_OUTPUT)) - return; /* no volume in capsrc, too */ - spec->vol_in_capsrc = 1; - } - - if (spec->num_adc_nids > 0) { - int mux = 0; - int num_adcs = 0; - - if (spec->input_mux && spec->input_mux->num_items > 1) - mux = 1; - if (spec->auto_mic) { - num_adcs = 1; - mux = 0; - } else if (spec->dyn_adc_switch) - num_adcs = 1; - if (!num_adcs) { - if (spec->num_adc_nids > 3) - spec->num_adc_nids = 3; - else if (!spec->num_adc_nids) - return; - num_adcs = spec->num_adc_nids; - } - spec->cap_mixer = caps[mux][num_adcs - 1]; - } -} - -/* - * standard auto-parser initializations - */ -static void alc_auto_init_std(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - alc_auto_init_multi_out(codec); - alc_auto_init_extra_out(codec); - alc_auto_init_analog_input(codec); - alc_auto_init_input_src(codec); - alc_auto_init_digital(codec); - if (spec->unsol_event) - alc_inithook(codec); -} - -/* - * Digital-beep handlers - */ -#ifdef CONFIG_SND_HDA_INPUT_BEEP -#define set_beep_amp(spec, nid, idx, dir) \ - ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir)) - -static const struct snd_pci_quirk beep_white_list[] = { - SND_PCI_QUIRK(0x1043, 0x829f, "ASUS", 1), - SND_PCI_QUIRK(0x1043, 0x83ce, "EeePC", 1), - SND_PCI_QUIRK(0x1043, 0x831a, "EeePC", 1), - SND_PCI_QUIRK(0x1043, 0x834a, "EeePC", 1), - SND_PCI_QUIRK(0x1458, 0xa002, "GA-MA790X", 1), - SND_PCI_QUIRK(0x8086, 0xd613, "Intel", 1), - {} -}; - -static inline int has_cdefine_beep(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - const struct snd_pci_quirk *q; - q = snd_pci_quirk_lookup(codec->bus->pci, beep_white_list); - if (q) - return q->value; - return spec->cdefine.enable_pcbeep; -} -#else -#define set_beep_amp(spec, nid, idx, dir) /* NOP */ -#define has_cdefine_beep(codec) 0 -#endif - -/* parse the BIOS configuration and set up the alc_spec */ -/* return 1 if successful, 0 if the proper config is not found, - * or a negative error code - */ -static int alc_parse_auto_config(struct hda_codec *codec, - const hda_nid_t *ignore_nids, - const hda_nid_t *ssid_nids) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int err; - - err = snd_hda_parse_pin_defcfg(codec, cfg, ignore_nids, - spec->parse_flags); - if (err < 0) - return err; - if (!cfg->line_outs) { - if (cfg->dig_outs || cfg->dig_in_pin) { - spec->multiout.max_channels = 2; - spec->no_analog = 1; - goto dig_only; - } - return 0; /* can't find valid BIOS pin config */ - } - - if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && - cfg->line_outs <= cfg->hp_outs) { - /* use HP as primary out */ - cfg->speaker_outs = cfg->line_outs; - memcpy(cfg->speaker_pins, cfg->line_out_pins, - sizeof(cfg->speaker_pins)); - cfg->line_outs = cfg->hp_outs; - memcpy(cfg->line_out_pins, cfg->hp_pins, sizeof(cfg->hp_pins)); - cfg->hp_outs = 0; - memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); - cfg->line_out_type = AUTO_PIN_HP_OUT; - } - - err = alc_auto_fill_dac_nids(codec); - if (err < 0) - return err; - err = alc_auto_add_multi_channel_mode(codec); - if (err < 0) - return err; - err = alc_auto_create_multi_out_ctls(codec, cfg); - if (err < 0) - return err; - err = alc_auto_create_hp_out(codec); - if (err < 0) - return err; - err = alc_auto_create_speaker_out(codec); - if (err < 0) - return err; - err = alc_auto_create_shared_input(codec); - if (err < 0) - return err; - err = alc_auto_create_input_ctls(codec); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - dig_only: - alc_auto_parse_digital(codec); - - if (!spec->no_analog) - alc_remove_invalid_adc_nids(codec); - - if (ssid_nids) - alc_ssid_check(codec, ssid_nids); - - if (!spec->no_analog) { - alc_auto_check_switches(codec); - err = alc_auto_add_mic_boost(codec); - if (err < 0) - return err; - } - - if (spec->kctls.list) - add_mixer(spec, spec->kctls.list); - - if (!spec->no_analog && !spec->cap_mixer) - set_capture_mixer(codec); - - return 1; -} - -static int alc880_parse_auto_config(struct hda_codec *codec) -{ - static const hda_nid_t alc880_ignore[] = { 0x1d, 0 }; - static const hda_nid_t alc880_ssids[] = { 0x15, 0x1b, 0x14, 0 }; - return alc_parse_auto_config(codec, alc880_ignore, alc880_ssids); -} - -/* - * ALC880 fix-ups - */ -enum { - ALC880_FIXUP_GPIO1, - ALC880_FIXUP_GPIO2, - ALC880_FIXUP_MEDION_RIM, - ALC880_FIXUP_LG, - ALC880_FIXUP_W810, - ALC880_FIXUP_EAPD_COEF, - ALC880_FIXUP_TCL_S700, - ALC880_FIXUP_VOL_KNOB, - ALC880_FIXUP_FUJITSU, - ALC880_FIXUP_F1734, - ALC880_FIXUP_UNIWILL, - ALC880_FIXUP_UNIWILL_DIG, - ALC880_FIXUP_Z71V, - ALC880_FIXUP_3ST_BASE, - ALC880_FIXUP_3ST, - ALC880_FIXUP_3ST_DIG, - ALC880_FIXUP_5ST_BASE, - ALC880_FIXUP_5ST, - ALC880_FIXUP_5ST_DIG, - ALC880_FIXUP_6ST_BASE, - ALC880_FIXUP_6ST, - ALC880_FIXUP_6ST_DIG, -}; - -/* enable the volume-knob widget support on NID 0x21 */ -static void alc880_fixup_vol_knob(struct hda_codec *codec, - const struct alc_fixup *fix, int action) -{ - if (action == ALC_FIXUP_ACT_PROBE) - snd_hda_jack_detect_enable(codec, 0x21, ALC_DCVOL_EVENT); -} - -static const struct alc_fixup alc880_fixups[] = { - [ALC880_FIXUP_GPIO1] = { - .type = ALC_FIXUP_VERBS, - .v.verbs = alc_gpio1_init_verbs, - }, - [ALC880_FIXUP_GPIO2] = { - .type = ALC_FIXUP_VERBS, - .v.verbs = alc_gpio2_init_verbs, - }, - [ALC880_FIXUP_MEDION_RIM] = { - .type = ALC_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 }, - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_GPIO2, - }, - [ALC880_FIXUP_LG] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - /* disable bogus unused pins */ - { 0x16, 0x411111f0 }, - { 0x18, 0x411111f0 }, - { 0x1a, 0x411111f0 }, - { } - } - }, - [ALC880_FIXUP_W810] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - /* disable bogus unused pins */ - { 0x17, 0x411111f0 }, - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_GPIO2, - }, - [ALC880_FIXUP_EAPD_COEF] = { - .type = ALC_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* change to EAPD mode */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 }, - {} - }, - }, - [ALC880_FIXUP_TCL_S700] = { - .type = ALC_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* change to EAPD mode */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 }, - {} - }, - .chained = true, - .chain_id = ALC880_FIXUP_GPIO2, - }, - [ALC880_FIXUP_VOL_KNOB] = { - .type = ALC_FIXUP_FUNC, - .v.func = alc880_fixup_vol_knob, - }, - [ALC880_FIXUP_FUJITSU] = { - /* override all pins as BIOS on old Amilo is broken */ - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x14, 0x0121411f }, /* HP */ - { 0x15, 0x99030120 }, /* speaker */ - { 0x16, 0x99030130 }, /* bass speaker */ - { 0x17, 0x411111f0 }, /* N/A */ - { 0x18, 0x411111f0 }, /* N/A */ - { 0x19, 0x01a19950 }, /* mic-in */ - { 0x1a, 0x411111f0 }, /* N/A */ - { 0x1b, 0x411111f0 }, /* N/A */ - { 0x1c, 0x411111f0 }, /* N/A */ - { 0x1d, 0x411111f0 }, /* N/A */ - { 0x1e, 0x01454140 }, /* SPDIF out */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_VOL_KNOB, - }, - [ALC880_FIXUP_F1734] = { - /* almost compatible with FUJITSU, but no bass and SPDIF */ - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x14, 0x0121411f }, /* HP */ - { 0x15, 0x99030120 }, /* speaker */ - { 0x16, 0x411111f0 }, /* N/A */ - { 0x17, 0x411111f0 }, /* N/A */ - { 0x18, 0x411111f0 }, /* N/A */ - { 0x19, 0x01a19950 }, /* mic-in */ - { 0x1a, 0x411111f0 }, /* N/A */ - { 0x1b, 0x411111f0 }, /* N/A */ - { 0x1c, 0x411111f0 }, /* N/A */ - { 0x1d, 0x411111f0 }, /* N/A */ - { 0x1e, 0x411111f0 }, /* N/A */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_VOL_KNOB, - }, - [ALC880_FIXUP_UNIWILL] = { - /* need to fix HP and speaker pins to be parsed correctly */ - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x14, 0x0121411f }, /* HP */ - { 0x15, 0x99030120 }, /* speaker */ - { 0x16, 0x99030130 }, /* bass speaker */ - { } - }, - }, - [ALC880_FIXUP_UNIWILL_DIG] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - /* disable bogus unused pins */ - { 0x17, 0x411111f0 }, - { 0x19, 0x411111f0 }, - { 0x1b, 0x411111f0 }, - { 0x1f, 0x411111f0 }, - { } - } - }, - [ALC880_FIXUP_Z71V] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - /* set up the whole pins as BIOS is utterly broken */ - { 0x14, 0x99030120 }, /* speaker */ - { 0x15, 0x0121411f }, /* HP */ - { 0x16, 0x411111f0 }, /* N/A */ - { 0x17, 0x411111f0 }, /* N/A */ - { 0x18, 0x01a19950 }, /* mic-in */ - { 0x19, 0x411111f0 }, /* N/A */ - { 0x1a, 0x01813031 }, /* line-in */ - { 0x1b, 0x411111f0 }, /* N/A */ - { 0x1c, 0x411111f0 }, /* N/A */ - { 0x1d, 0x411111f0 }, /* N/A */ - { 0x1e, 0x0144111e }, /* SPDIF */ - { } - } - }, - [ALC880_FIXUP_3ST_BASE] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x14, 0x01014010 }, /* line-out */ - { 0x15, 0x411111f0 }, /* N/A */ - { 0x16, 0x411111f0 }, /* N/A */ - { 0x17, 0x411111f0 }, /* N/A */ - { 0x18, 0x01a19c30 }, /* mic-in */ - { 0x19, 0x0121411f }, /* HP */ - { 0x1a, 0x01813031 }, /* line-in */ - { 0x1b, 0x02a19c40 }, /* front-mic */ - { 0x1c, 0x411111f0 }, /* N/A */ - { 0x1d, 0x411111f0 }, /* N/A */ - /* 0x1e is filled in below */ - { 0x1f, 0x411111f0 }, /* N/A */ - { } - } - }, - [ALC880_FIXUP_3ST] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x1e, 0x411111f0 }, /* N/A */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_3ST_BASE, - }, - [ALC880_FIXUP_3ST_DIG] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x1e, 0x0144111e }, /* SPDIF */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_3ST_BASE, - }, - [ALC880_FIXUP_5ST_BASE] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x14, 0x01014010 }, /* front */ - { 0x15, 0x411111f0 }, /* N/A */ - { 0x16, 0x01011411 }, /* CLFE */ - { 0x17, 0x01016412 }, /* surr */ - { 0x18, 0x01a19c30 }, /* mic-in */ - { 0x19, 0x0121411f }, /* HP */ - { 0x1a, 0x01813031 }, /* line-in */ - { 0x1b, 0x02a19c40 }, /* front-mic */ - { 0x1c, 0x411111f0 }, /* N/A */ - { 0x1d, 0x411111f0 }, /* N/A */ - /* 0x1e is filled in below */ - { 0x1f, 0x411111f0 }, /* N/A */ - { } - } - }, - [ALC880_FIXUP_5ST] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x1e, 0x411111f0 }, /* N/A */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_5ST_BASE, - }, - [ALC880_FIXUP_5ST_DIG] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x1e, 0x0144111e }, /* SPDIF */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_5ST_BASE, - }, - [ALC880_FIXUP_6ST_BASE] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x14, 0x01014010 }, /* front */ - { 0x15, 0x01016412 }, /* surr */ - { 0x16, 0x01011411 }, /* CLFE */ - { 0x17, 0x01012414 }, /* side */ - { 0x18, 0x01a19c30 }, /* mic-in */ - { 0x19, 0x02a19c40 }, /* front-mic */ - { 0x1a, 0x01813031 }, /* line-in */ - { 0x1b, 0x0121411f }, /* HP */ - { 0x1c, 0x411111f0 }, /* N/A */ - { 0x1d, 0x411111f0 }, /* N/A */ - /* 0x1e is filled in below */ - { 0x1f, 0x411111f0 }, /* N/A */ - { } - } - }, - [ALC880_FIXUP_6ST] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x1e, 0x411111f0 }, /* N/A */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_6ST_BASE, - }, - [ALC880_FIXUP_6ST_DIG] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x1e, 0x0144111e }, /* SPDIF */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_6ST_BASE, - }, -}; - -static const struct snd_pci_quirk alc880_fixup_tbl[] = { - SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_FIXUP_W810), - SND_PCI_QUIRK(0x1043, 0x1964, "ASUS Z71V", ALC880_FIXUP_Z71V), - SND_PCI_QUIRK_VENDOR(0x1043, "ASUS", ALC880_FIXUP_GPIO1), - SND_PCI_QUIRK(0x1558, 0x5401, "Clevo GPIO2", ALC880_FIXUP_GPIO2), - SND_PCI_QUIRK_VENDOR(0x1558, "Clevo", ALC880_FIXUP_EAPD_COEF), - SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_FIXUP_UNIWILL_DIG), - SND_PCI_QUIRK(0x1584, 0x9054, "Uniwill", ALC880_FIXUP_F1734), - SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_FIXUP_UNIWILL), - SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_FIXUP_VOL_KNOB), - SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_FIXUP_W810), - SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_FIXUP_MEDION_RIM), - SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_FIXUP_F1734), - SND_PCI_QUIRK(0x1734, 0x1094, "FSC Amilo M1451G", ALC880_FIXUP_FUJITSU), - SND_PCI_QUIRK(0x1734, 0x10ac, "FSC AMILO Xi 1526", ALC880_FIXUP_F1734), - SND_PCI_QUIRK(0x1734, 0x10b0, "FSC Amilo Pi1556", ALC880_FIXUP_FUJITSU), - SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_FIXUP_LG), - SND_PCI_QUIRK(0x1854, 0x005f, "LG P1 Express", ALC880_FIXUP_LG), - SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_FIXUP_LG), - SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_FIXUP_TCL_S700), - - /* Below is the copied entries from alc880_quirks.c. - * It's not quite sure whether BIOS sets the correct pin-config table - * on these machines, thus they are kept to be compatible with - * the old static quirks. Once when it's confirmed to work without - * these overrides, it'd be better to remove. - */ - SND_PCI_QUIRK(0x1019, 0xa880, "ECS", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x1019, 0xa884, "Acer APFV", ALC880_FIXUP_6ST), - SND_PCI_QUIRK(0x1025, 0x0070, "ULI", ALC880_FIXUP_3ST_DIG), - SND_PCI_QUIRK(0x1025, 0x0077, "ULI", ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x1025, 0x0078, "ULI", ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x1025, 0x0087, "ULI", ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x1025, 0xe309, "ULI", ALC880_FIXUP_3ST_DIG), - SND_PCI_QUIRK(0x1025, 0xe310, "ULI", ALC880_FIXUP_3ST), - SND_PCI_QUIRK(0x1039, 0x1234, NULL, ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x104d, 0x81a0, "Sony", ALC880_FIXUP_3ST), - SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_FIXUP_3ST), - SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_FIXUP_5ST), - SND_PCI_QUIRK(0x107b, 0x3033, "Gateway", ALC880_FIXUP_5ST), - SND_PCI_QUIRK(0x107b, 0x4039, "Gateway", ALC880_FIXUP_5ST), - SND_PCI_QUIRK(0x1297, 0xc790, "Shuttle ST20G5", ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x1458, 0xa102, "Gigabyte K8", ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x1150, "MSI", ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x1509, 0x925d, "FIC P4M", ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_FIXUP_6ST_DIG), /* broken BIOS */ - SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xd400, "Intel mobo", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xd401, "Intel mobo", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_FIXUP_3ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe224, "Intel mobo", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_FIXUP_3ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_FIXUP_3ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe400, "Intel mobo", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe401, "Intel mobo", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe402, "Intel mobo", ALC880_FIXUP_5ST_DIG), - /* default Intel */ - SND_PCI_QUIRK_VENDOR(0x8086, "Intel mobo", ALC880_FIXUP_3ST), - SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_FIXUP_6ST_DIG), - {} -}; - -static const struct alc_model_fixup alc880_fixup_models[] = { - {.id = ALC880_FIXUP_3ST, .name = "3stack"}, - {.id = ALC880_FIXUP_3ST_DIG, .name = "3stack-digout"}, - {.id = ALC880_FIXUP_5ST, .name = "5stack"}, - {.id = ALC880_FIXUP_5ST_DIG, .name = "5stack-digout"}, - {.id = ALC880_FIXUP_6ST, .name = "6stack"}, - {.id = ALC880_FIXUP_6ST_DIG, .name = "6stack-digout"}, - {} -}; - - -/* - * OK, here we have finally the patch for ALC880 - */ -static int patch_alc880(struct hda_codec *codec) -{ - struct alc_spec *spec; - int err; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - - spec->mixer_nid = 0x0b; - spec->need_dac_fix = 1; - - alc_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl, - alc880_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); - - /* automatic parse from the BIOS config */ - err = alc880_parse_auto_config(codec); - if (err < 0) - goto error; - - if (!spec->no_analog) { - err = snd_hda_attach_beep_device(codec, 0x1); - if (err < 0) - goto error; - set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); - } - - codec->patch_ops = alc_patch_ops; - - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); - - return 0; - - error: - alc_free(codec); - return err; -} - - -/* - * ALC260 support - */ -static int alc260_parse_auto_config(struct hda_codec *codec) -{ - static const hda_nid_t alc260_ignore[] = { 0x17, 0 }; - static const hda_nid_t alc260_ssids[] = { 0x10, 0x15, 0x0f, 0 }; - return alc_parse_auto_config(codec, alc260_ignore, alc260_ssids); -} - -/* - * Pin config fixes - */ -enum { - ALC260_FIXUP_HP_DC5750, - ALC260_FIXUP_HP_PIN_0F, - ALC260_FIXUP_COEF, - ALC260_FIXUP_GPIO1, - ALC260_FIXUP_GPIO1_TOGGLE, - ALC260_FIXUP_REPLACER, - ALC260_FIXUP_HP_B1900, - ALC260_FIXUP_KN1, -}; - -static void alc260_gpio1_automute(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - spec->hp_jack_present); -} - -static void alc260_fixup_gpio1_toggle(struct hda_codec *codec, - const struct alc_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - if (action == ALC_FIXUP_ACT_PROBE) { - /* although the machine has only one output pin, we need to - * toggle GPIO1 according to the jack state - */ - spec->automute_hook = alc260_gpio1_automute; - spec->detect_hp = 1; - spec->automute_speaker = 1; - spec->autocfg.hp_pins[0] = 0x0f; /* copy it for automute */ - snd_hda_jack_detect_enable(codec, 0x0f, ALC_HP_EVENT); - spec->unsol_event = alc_sku_unsol_event; - add_verb(codec->spec, alc_gpio1_init_verbs); - } -} - -static void alc260_fixup_kn1(struct hda_codec *codec, - const struct alc_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - static const struct alc_pincfg pincfgs[] = { - { 0x0f, 0x02214000 }, /* HP/speaker */ - { 0x12, 0x90a60160 }, /* int mic */ - { 0x13, 0x02a19000 }, /* ext mic */ - { 0x18, 0x01446000 }, /* SPDIF out */ - /* disable bogus I/O pins */ - { 0x10, 0x411111f0 }, - { 0x11, 0x411111f0 }, - { 0x14, 0x411111f0 }, - { 0x15, 0x411111f0 }, - { 0x16, 0x411111f0 }, - { 0x17, 0x411111f0 }, - { 0x19, 0x411111f0 }, - { } - }; - - switch (action) { - case ALC_FIXUP_ACT_PRE_PROBE: - alc_apply_pincfgs(codec, pincfgs); - break; - case ALC_FIXUP_ACT_PROBE: - spec->init_amp = ALC_INIT_NONE; - break; - } -} - -static const struct alc_fixup alc260_fixups[] = { - [ALC260_FIXUP_HP_DC5750] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x11, 0x90130110 }, /* speaker */ - { } - } - }, - [ALC260_FIXUP_HP_PIN_0F] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x0f, 0x01214000 }, /* HP */ - { } - } - }, - [ALC260_FIXUP_COEF] = { - .type = ALC_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3040 }, - { } - }, - .chained = true, - .chain_id = ALC260_FIXUP_HP_PIN_0F, - }, - [ALC260_FIXUP_GPIO1] = { - .type = ALC_FIXUP_VERBS, - .v.verbs = alc_gpio1_init_verbs, - }, - [ALC260_FIXUP_GPIO1_TOGGLE] = { - .type = ALC_FIXUP_FUNC, - .v.func = alc260_fixup_gpio1_toggle, - .chained = true, - .chain_id = ALC260_FIXUP_HP_PIN_0F, - }, - [ALC260_FIXUP_REPLACER] = { - .type = ALC_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 }, - { } - }, - .chained = true, - .chain_id = ALC260_FIXUP_GPIO1_TOGGLE, - }, - [ALC260_FIXUP_HP_B1900] = { - .type = ALC_FIXUP_FUNC, - .v.func = alc260_fixup_gpio1_toggle, - .chained = true, - .chain_id = ALC260_FIXUP_COEF, - }, - [ALC260_FIXUP_KN1] = { - .type = ALC_FIXUP_FUNC, - .v.func = alc260_fixup_kn1, - }, -}; - -static const struct snd_pci_quirk alc260_fixup_tbl[] = { - SND_PCI_QUIRK(0x1025, 0x007b, "Acer C20x", ALC260_FIXUP_GPIO1), - SND_PCI_QUIRK(0x1025, 0x007f, "Acer Aspire 9500", ALC260_FIXUP_COEF), - SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_FIXUP_GPIO1), - SND_PCI_QUIRK(0x103c, 0x280a, "HP dc5750", ALC260_FIXUP_HP_DC5750), - SND_PCI_QUIRK(0x103c, 0x30ba, "HP Presario B1900", ALC260_FIXUP_HP_B1900), - SND_PCI_QUIRK(0x1509, 0x4540, "Favorit 100XS", ALC260_FIXUP_GPIO1), - SND_PCI_QUIRK(0x152d, 0x0729, "Quanta KN1", ALC260_FIXUP_KN1), - SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V", ALC260_FIXUP_REPLACER), - SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_FIXUP_COEF), - {} -}; - -/* - */ -static int patch_alc260(struct hda_codec *codec) -{ - struct alc_spec *spec; - int err; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - - spec->mixer_nid = 0x07; - - alc_pick_fixup(codec, NULL, alc260_fixup_tbl, alc260_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); - - /* automatic parse from the BIOS config */ - err = alc260_parse_auto_config(codec); - if (err < 0) - goto error; - - if (!spec->no_analog) { - err = snd_hda_attach_beep_device(codec, 0x1); - if (err < 0) - goto error; - set_beep_amp(spec, 0x07, 0x05, HDA_INPUT); - } - - codec->patch_ops = alc_patch_ops; - spec->shutup = alc_eapd_shutup; - - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); - - return 0; - - error: - alc_free(codec); - return err; -} - - -/* - * ALC882/883/885/888/889 support - * - * ALC882 is almost identical with ALC880 but has cleaner and more flexible - * configuration. Each pin widget can choose any input DACs and a mixer. - * Each ADC is connected from a mixer of all inputs. This makes possible - * 6-channel independent captures. - * - * In addition, an independent DAC for the multi-playback (not used in this - * driver yet). - */ - -/* - * Pin config fixes - */ -enum { - ALC882_FIXUP_ABIT_AW9D_MAX, - ALC882_FIXUP_LENOVO_Y530, - ALC882_FIXUP_PB_M5210, - ALC882_FIXUP_ACER_ASPIRE_7736, - ALC882_FIXUP_ASUS_W90V, - ALC889_FIXUP_CD, - ALC889_FIXUP_VAIO_TT, - ALC888_FIXUP_EEE1601, - ALC882_FIXUP_EAPD, - ALC883_FIXUP_EAPD, - ALC883_FIXUP_ACER_EAPD, - ALC882_FIXUP_GPIO1, - ALC882_FIXUP_GPIO2, - ALC882_FIXUP_GPIO3, - ALC889_FIXUP_COEF, - ALC882_FIXUP_ASUS_W2JC, - ALC882_FIXUP_ACER_ASPIRE_4930G, - ALC882_FIXUP_ACER_ASPIRE_8930G, - ALC882_FIXUP_ASPIRE_8930G_VERBS, - ALC885_FIXUP_MACPRO_GPIO, - ALC889_FIXUP_DAC_ROUTE, - ALC889_FIXUP_MBP_VREF, - ALC889_FIXUP_IMAC91_VREF, -}; - -static void alc889_fixup_coef(struct hda_codec *codec, - const struct alc_fixup *fix, int action) -{ - if (action != ALC_FIXUP_ACT_INIT) - return; - alc889_coef_init(codec); -} - -/* toggle speaker-output according to the hp-jack state */ -static void alc882_gpio_mute(struct hda_codec *codec, int pin, int muted) -{ - unsigned int gpiostate, gpiomask, gpiodir; - - gpiostate = snd_hda_codec_read(codec, codec->afg, 0, - AC_VERB_GET_GPIO_DATA, 0); - - if (!muted) - gpiostate |= (1 << pin); - else - gpiostate &= ~(1 << pin); - - gpiomask = snd_hda_codec_read(codec, codec->afg, 0, - AC_VERB_GET_GPIO_MASK, 0); - gpiomask |= (1 << pin); - - gpiodir = snd_hda_codec_read(codec, codec->afg, 0, - AC_VERB_GET_GPIO_DIRECTION, 0); - gpiodir |= (1 << pin); - - - snd_hda_codec_write(codec, codec->afg, 0, - AC_VERB_SET_GPIO_MASK, gpiomask); - snd_hda_codec_write(codec, codec->afg, 0, - AC_VERB_SET_GPIO_DIRECTION, gpiodir); - - msleep(1); - - snd_hda_codec_write(codec, codec->afg, 0, - AC_VERB_SET_GPIO_DATA, gpiostate); -} - -/* set up GPIO at initialization */ -static void alc885_fixup_macpro_gpio(struct hda_codec *codec, - const struct alc_fixup *fix, int action) -{ - if (action != ALC_FIXUP_ACT_INIT) - return; - alc882_gpio_mute(codec, 0, 0); - alc882_gpio_mute(codec, 1, 0); -} - -/* Fix the connection of some pins for ALC889: - * At least, Acer Aspire 5935 shows the connections to DAC3/4 don't - * work correctly (bko#42740) - */ -static void alc889_fixup_dac_route(struct hda_codec *codec, - const struct alc_fixup *fix, int action) -{ - if (action == ALC_FIXUP_ACT_PRE_PROBE) { - /* fake the connections during parsing the tree */ - hda_nid_t conn1[2] = { 0x0c, 0x0d }; - hda_nid_t conn2[2] = { 0x0e, 0x0f }; - snd_hda_override_conn_list(codec, 0x14, 2, conn1); - snd_hda_override_conn_list(codec, 0x15, 2, conn1); - snd_hda_override_conn_list(codec, 0x18, 2, conn2); - snd_hda_override_conn_list(codec, 0x1a, 2, conn2); - } else if (action == ALC_FIXUP_ACT_PROBE) { - /* restore the connections */ - hda_nid_t conn[5] = { 0x0c, 0x0d, 0x0e, 0x0f, 0x26 }; - snd_hda_override_conn_list(codec, 0x14, 5, conn); - snd_hda_override_conn_list(codec, 0x15, 5, conn); - snd_hda_override_conn_list(codec, 0x18, 5, conn); - snd_hda_override_conn_list(codec, 0x1a, 5, conn); - } -} - -/* Set VREF on HP pin */ -static void alc889_fixup_mbp_vref(struct hda_codec *codec, - const struct alc_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - static hda_nid_t nids[2] = { 0x14, 0x15 }; - int i; - - if (action != ALC_FIXUP_ACT_INIT) - return; - for (i = 0; i < ARRAY_SIZE(nids); i++) { - unsigned int val = snd_hda_codec_get_pincfg(codec, nids[i]); - if (get_defcfg_device(val) != AC_JACK_HP_OUT) - continue; - val = snd_hda_codec_read(codec, nids[i], 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - val |= AC_PINCTL_VREF_80; - snd_hda_codec_write(codec, nids[i], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, val); - spec->keep_vref_in_automute = 1; - break; - } -} - -/* Set VREF on speaker pins on imac91 */ -static void alc889_fixup_imac91_vref(struct hda_codec *codec, - const struct alc_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - static hda_nid_t nids[2] = { 0x18, 0x1a }; - int i; - - if (action != ALC_FIXUP_ACT_INIT) - return; - for (i = 0; i < ARRAY_SIZE(nids); i++) { - unsigned int val; - val = snd_hda_codec_read(codec, nids[i], 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - val |= AC_PINCTL_VREF_50; - snd_hda_codec_write(codec, nids[i], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, val); - } - spec->keep_vref_in_automute = 1; -} - -static const struct alc_fixup alc882_fixups[] = { - [ALC882_FIXUP_ABIT_AW9D_MAX] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x15, 0x01080104 }, /* side */ - { 0x16, 0x01011012 }, /* rear */ - { 0x17, 0x01016011 }, /* clfe */ - { } - } - }, - [ALC882_FIXUP_LENOVO_Y530] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x15, 0x99130112 }, /* rear int speakers */ - { 0x16, 0x99130111 }, /* subwoofer */ - { } - } - }, - [ALC882_FIXUP_PB_M5210] = { - .type = ALC_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 }, - {} - } - }, - [ALC882_FIXUP_ACER_ASPIRE_7736] = { - .type = ALC_FIXUP_SKU, - .v.sku = ALC_FIXUP_SKU_IGNORE, - }, - [ALC882_FIXUP_ASUS_W90V] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x16, 0x99130110 }, /* fix sequence for CLFE */ - { } - } - }, - [ALC889_FIXUP_CD] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x1c, 0x993301f0 }, /* CD */ - { } - } - }, - [ALC889_FIXUP_VAIO_TT] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x17, 0x90170111 }, /* hidden surround speaker */ - { } - } - }, - [ALC888_FIXUP_EEE1601] = { - .type = ALC_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x0b }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0838 }, - { } - } - }, - [ALC882_FIXUP_EAPD] = { - .type = ALC_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* change to EAPD mode */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 }, - { } - } - }, - [ALC883_FIXUP_EAPD] = { - .type = ALC_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* change to EAPD mode */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 }, - { } - } - }, - [ALC883_FIXUP_ACER_EAPD] = { - .type = ALC_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* eanable EAPD on Acer laptops */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 }, - { } - } - }, - [ALC882_FIXUP_GPIO1] = { - .type = ALC_FIXUP_VERBS, - .v.verbs = alc_gpio1_init_verbs, - }, - [ALC882_FIXUP_GPIO2] = { - .type = ALC_FIXUP_VERBS, - .v.verbs = alc_gpio2_init_verbs, - }, - [ALC882_FIXUP_GPIO3] = { - .type = ALC_FIXUP_VERBS, - .v.verbs = alc_gpio3_init_verbs, - }, - [ALC882_FIXUP_ASUS_W2JC] = { - .type = ALC_FIXUP_VERBS, - .v.verbs = alc_gpio1_init_verbs, - .chained = true, - .chain_id = ALC882_FIXUP_EAPD, - }, - [ALC889_FIXUP_COEF] = { - .type = ALC_FIXUP_FUNC, - .v.func = alc889_fixup_coef, - }, - [ALC882_FIXUP_ACER_ASPIRE_4930G] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x16, 0x99130111 }, /* CLFE speaker */ - { 0x17, 0x99130112 }, /* surround speaker */ - { } - }, - .chained = true, - .chain_id = ALC882_FIXUP_GPIO1, - }, - [ALC882_FIXUP_ACER_ASPIRE_8930G] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x16, 0x99130111 }, /* CLFE speaker */ - { 0x1b, 0x99130112 }, /* surround speaker */ - { } - }, - .chained = true, - .chain_id = ALC882_FIXUP_ASPIRE_8930G_VERBS, - }, - [ALC882_FIXUP_ASPIRE_8930G_VERBS] = { - /* additional init verbs for Acer Aspire 8930G */ - .type = ALC_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* Enable all DACs */ - /* DAC DISABLE/MUTE 1? */ - /* setting bits 1-5 disables DAC nids 0x02-0x06 - * apparently. Init=0x38 */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x03 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, - /* DAC DISABLE/MUTE 2? */ - /* some bit here disables the other DACs. - * Init=0x4900 */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x08 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, - /* DMIC fix - * This laptop has a stereo digital microphone. - * The mics are only 1cm apart which makes the stereo - * useless. However, either the mic or the ALC889 - * makes the signal become a difference/sum signal - * instead of standard stereo, which is annoying. - * So instead we flip this bit which makes the - * codec replicate the sum signal to both channels, - * turning it into a normal mono mic. - */ - /* DMIC_CONTROL? Init value = 0x0001 */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x0b }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0003 }, - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 }, - { } - }, - .chained = true, - .chain_id = ALC882_FIXUP_GPIO1, - }, - [ALC885_FIXUP_MACPRO_GPIO] = { - .type = ALC_FIXUP_FUNC, - .v.func = alc885_fixup_macpro_gpio, - }, - [ALC889_FIXUP_DAC_ROUTE] = { - .type = ALC_FIXUP_FUNC, - .v.func = alc889_fixup_dac_route, - }, - [ALC889_FIXUP_MBP_VREF] = { - .type = ALC_FIXUP_FUNC, - .v.func = alc889_fixup_mbp_vref, - .chained = true, - .chain_id = ALC882_FIXUP_GPIO1, - }, - [ALC889_FIXUP_IMAC91_VREF] = { - .type = ALC_FIXUP_FUNC, - .v.func = alc889_fixup_imac91_vref, - .chained = true, - .chain_id = ALC882_FIXUP_GPIO1, - }, -}; - -static const struct snd_pci_quirk alc882_fixup_tbl[] = { - SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_FIXUP_ACER_EAPD), - SND_PCI_QUIRK(0x1025, 0x0090, "Acer Aspire", ALC883_FIXUP_ACER_EAPD), - SND_PCI_QUIRK(0x1025, 0x010a, "Acer Ferrari 5000", ALC883_FIXUP_ACER_EAPD), - SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_FIXUP_ACER_EAPD), - SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_FIXUP_ACER_EAPD), - SND_PCI_QUIRK(0x1025, 0x0121, "Acer Aspire 5920G", ALC883_FIXUP_ACER_EAPD), - SND_PCI_QUIRK(0x1025, 0x013e, "Acer Aspire 4930G", - ALC882_FIXUP_ACER_ASPIRE_4930G), - SND_PCI_QUIRK(0x1025, 0x013f, "Acer Aspire 5930G", - ALC882_FIXUP_ACER_ASPIRE_4930G), - SND_PCI_QUIRK(0x1025, 0x0145, "Acer Aspire 8930G", - ALC882_FIXUP_ACER_ASPIRE_8930G), - SND_PCI_QUIRK(0x1025, 0x0146, "Acer Aspire 6935G", - ALC882_FIXUP_ACER_ASPIRE_8930G), - SND_PCI_QUIRK(0x1025, 0x015e, "Acer Aspire 6930G", - ALC882_FIXUP_ACER_ASPIRE_4930G), - SND_PCI_QUIRK(0x1025, 0x0166, "Acer Aspire 6530G", - ALC882_FIXUP_ACER_ASPIRE_4930G), - SND_PCI_QUIRK(0x1025, 0x0142, "Acer Aspire 7730G", - ALC882_FIXUP_ACER_ASPIRE_4930G), - SND_PCI_QUIRK(0x1025, 0x0155, "Packard-Bell M5120", ALC882_FIXUP_PB_M5210), - SND_PCI_QUIRK(0x1025, 0x021e, "Acer Aspire 5739G", - ALC882_FIXUP_ACER_ASPIRE_4930G), - SND_PCI_QUIRK(0x1025, 0x0259, "Acer Aspire 5935", ALC889_FIXUP_DAC_ROUTE), - SND_PCI_QUIRK(0x1025, 0x026b, "Acer Aspire 8940G", ALC882_FIXUP_ACER_ASPIRE_8930G), - SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", ALC882_FIXUP_ACER_ASPIRE_7736), - SND_PCI_QUIRK(0x1043, 0x13c2, "Asus A7M", ALC882_FIXUP_EAPD), - SND_PCI_QUIRK(0x1043, 0x1873, "ASUS W90V", ALC882_FIXUP_ASUS_W90V), - SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_FIXUP_ASUS_W2JC), - SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_FIXUP_EEE1601), - SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC889_FIXUP_VAIO_TT), - - /* All Apple entries are in codec SSIDs */ - SND_PCI_QUIRK(0x106b, 0x00a0, "MacBookPro 3,1", ALC889_FIXUP_MBP_VREF), - SND_PCI_QUIRK(0x106b, 0x00a1, "Macbook", ALC889_FIXUP_MBP_VREF), - SND_PCI_QUIRK(0x106b, 0x00a4, "MacbookPro 4,1", ALC889_FIXUP_MBP_VREF), - SND_PCI_QUIRK(0x106b, 0x0c00, "Mac Pro", ALC885_FIXUP_MACPRO_GPIO), - SND_PCI_QUIRK(0x106b, 0x1000, "iMac 24", ALC885_FIXUP_MACPRO_GPIO), - SND_PCI_QUIRK(0x106b, 0x2800, "AppleTV", ALC885_FIXUP_MACPRO_GPIO), - SND_PCI_QUIRK(0x106b, 0x2c00, "MacbookPro rev3", ALC889_FIXUP_MBP_VREF), - SND_PCI_QUIRK(0x106b, 0x3000, "iMac", ALC889_FIXUP_MBP_VREF), - SND_PCI_QUIRK(0x106b, 0x3200, "iMac 7,1 Aluminum", ALC882_FIXUP_EAPD), - SND_PCI_QUIRK(0x106b, 0x3400, "MacBookAir 1,1", ALC889_FIXUP_MBP_VREF), - SND_PCI_QUIRK(0x106b, 0x3500, "MacBookAir 2,1", ALC889_FIXUP_MBP_VREF), - SND_PCI_QUIRK(0x106b, 0x3600, "Macbook 3,1", ALC889_FIXUP_MBP_VREF), - SND_PCI_QUIRK(0x106b, 0x3800, "MacbookPro 4,1", ALC889_FIXUP_MBP_VREF), - SND_PCI_QUIRK(0x106b, 0x3e00, "iMac 24 Aluminum", ALC885_FIXUP_MACPRO_GPIO), - SND_PCI_QUIRK(0x106b, 0x3f00, "Macbook 5,1", ALC889_FIXUP_IMAC91_VREF), - SND_PCI_QUIRK(0x106b, 0x4000, "MacbookPro 5,1", ALC889_FIXUP_IMAC91_VREF), - SND_PCI_QUIRK(0x106b, 0x4100, "Macmini 3,1", ALC889_FIXUP_IMAC91_VREF), - SND_PCI_QUIRK(0x106b, 0x4200, "Mac Pro 5,1", ALC885_FIXUP_MACPRO_GPIO), - SND_PCI_QUIRK(0x106b, 0x4600, "MacbookPro 5,2", ALC889_FIXUP_IMAC91_VREF), - SND_PCI_QUIRK(0x106b, 0x4900, "iMac 9,1 Aluminum", ALC889_FIXUP_IMAC91_VREF), - SND_PCI_QUIRK(0x106b, 0x4a00, "Macbook 5,2", ALC889_FIXUP_IMAC91_VREF), - - SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC882_FIXUP_EAPD), - SND_PCI_QUIRK(0x1462, 0x7350, "MSI-7350", ALC889_FIXUP_CD), - SND_PCI_QUIRK_VENDOR(0x1462, "MSI", ALC882_FIXUP_GPIO3), - SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte EP45-DS3", ALC889_FIXUP_CD), - SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", ALC882_FIXUP_ABIT_AW9D_MAX), - SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC882_FIXUP_EAPD), - SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_FIXUP_EAPD), - SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", ALC882_FIXUP_LENOVO_Y530), - SND_PCI_QUIRK(0x8086, 0x0022, "DX58SO", ALC889_FIXUP_COEF), - {} -}; - -static const struct alc_model_fixup alc882_fixup_models[] = { - {.id = ALC882_FIXUP_ACER_ASPIRE_4930G, .name = "acer-aspire-4930g"}, - {.id = ALC882_FIXUP_ACER_ASPIRE_8930G, .name = "acer-aspire-8930g"}, - {.id = ALC883_FIXUP_ACER_EAPD, .name = "acer-aspire"}, - {} -}; - -/* - * BIOS auto configuration - */ -/* almost identical with ALC880 parser... */ -static int alc882_parse_auto_config(struct hda_codec *codec) -{ - static const hda_nid_t alc882_ignore[] = { 0x1d, 0 }; - static const hda_nid_t alc882_ssids[] = { 0x15, 0x1b, 0x14, 0 }; - return alc_parse_auto_config(codec, alc882_ignore, alc882_ssids); -} - -/* - */ -static int patch_alc882(struct hda_codec *codec) -{ - struct alc_spec *spec; - int err; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - - spec->mixer_nid = 0x0b; - - switch (codec->vendor_id) { - case 0x10ec0882: - case 0x10ec0885: - break; - default: - /* ALC883 and variants */ - alc_fix_pll_init(codec, 0x20, 0x0a, 10); - break; - } - - err = alc_codec_rename_from_preset(codec); - if (err < 0) - goto error; - - alc_pick_fixup(codec, alc882_fixup_models, alc882_fixup_tbl, - alc882_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); - - alc_auto_parse_customize_define(codec); - - /* automatic parse from the BIOS config */ - err = alc882_parse_auto_config(codec); - if (err < 0) - goto error; - - if (!spec->no_analog && has_cdefine_beep(codec)) { - err = snd_hda_attach_beep_device(codec, 0x1); - if (err < 0) - goto error; - set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); - } - - codec->patch_ops = alc_patch_ops; - - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); - - return 0; - - error: - alc_free(codec); - return err; -} - - -/* - * ALC262 support - */ -static int alc262_parse_auto_config(struct hda_codec *codec) -{ - static const hda_nid_t alc262_ignore[] = { 0x1d, 0 }; - static const hda_nid_t alc262_ssids[] = { 0x15, 0x1b, 0x14, 0 }; - return alc_parse_auto_config(codec, alc262_ignore, alc262_ssids); -} - -/* - * Pin config fixes - */ -enum { - ALC262_FIXUP_FSC_H270, - ALC262_FIXUP_HP_Z200, - ALC262_FIXUP_TYAN, - ALC262_FIXUP_LENOVO_3000, - ALC262_FIXUP_BENQ, - ALC262_FIXUP_BENQ_T31, -}; - -static const struct alc_fixup alc262_fixups[] = { - [ALC262_FIXUP_FSC_H270] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x15, 0x0221142f }, /* front HP */ - { 0x1b, 0x0121141f }, /* rear HP */ - { } - } - }, - [ALC262_FIXUP_HP_Z200] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x16, 0x99130120 }, /* internal speaker */ - { } - } - }, - [ALC262_FIXUP_TYAN] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x14, 0x1993e1f0 }, /* int AUX */ - { } - } - }, - [ALC262_FIXUP_LENOVO_3000] = { - .type = ALC_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 }, - {} - }, - .chained = true, - .chain_id = ALC262_FIXUP_BENQ, - }, - [ALC262_FIXUP_BENQ] = { - .type = ALC_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 }, - {} - } - }, - [ALC262_FIXUP_BENQ_T31] = { - .type = ALC_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 }, - {} - } - }, -}; - -static const struct snd_pci_quirk alc262_fixup_tbl[] = { - SND_PCI_QUIRK(0x103c, 0x170b, "HP Z200", ALC262_FIXUP_HP_Z200), - SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FIXUP_BENQ), - SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FIXUP_BENQ), - SND_PCI_QUIRK(0x10f1, 0x2915, "Tyan Thunder n6650W", ALC262_FIXUP_TYAN), - SND_PCI_QUIRK(0x1734, 0x1147, "FSC Celsius H270", ALC262_FIXUP_FSC_H270), - SND_PCI_QUIRK(0x17aa, 0x384e, "Lenovo 3000", ALC262_FIXUP_LENOVO_3000), - SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_FIXUP_BENQ), - SND_PCI_QUIRK(0x17ff, 0x058d, "Benq T31-16", ALC262_FIXUP_BENQ_T31), - {} -}; - - -/* - */ -static int patch_alc262(struct hda_codec *codec) -{ - struct alc_spec *spec; - int err; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - - spec->mixer_nid = 0x0b; - -#if 0 - /* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is - * under-run - */ - { - int tmp; - snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7); - tmp = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_PROC_COEF, 0); - snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7); - snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PROC_COEF, tmp | 0x80); - } -#endif - alc_fix_pll_init(codec, 0x20, 0x0a, 10); - - alc_pick_fixup(codec, NULL, alc262_fixup_tbl, alc262_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); - - alc_auto_parse_customize_define(codec); - - /* automatic parse from the BIOS config */ - err = alc262_parse_auto_config(codec); - if (err < 0) - goto error; - - if (!spec->no_analog && has_cdefine_beep(codec)) { - err = snd_hda_attach_beep_device(codec, 0x1); - if (err < 0) - goto error; - set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); - } - - codec->patch_ops = alc_patch_ops; - spec->shutup = alc_eapd_shutup; - - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); - - return 0; - - error: - alc_free(codec); - return err; -} - -/* - * ALC268 - */ -/* bind Beep switches of both NID 0x0f and 0x10 */ -static const struct hda_bind_ctls alc268_bind_beep_sw = { - .ops = &snd_hda_bind_sw, - .values = { - HDA_COMPOSE_AMP_VAL(0x0f, 3, 1, HDA_INPUT), - HDA_COMPOSE_AMP_VAL(0x10, 3, 1, HDA_INPUT), - 0 - }, -}; - -static const struct snd_kcontrol_new alc268_beep_mixer[] = { - HDA_CODEC_VOLUME("Beep Playback Volume", 0x1d, 0x0, HDA_INPUT), - HDA_BIND_SW("Beep Playback Switch", &alc268_bind_beep_sw), - { } -}; - -/* set PCBEEP vol = 0, mute connections */ -static const struct hda_verb alc268_beep_init_verbs[] = { - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - { } -}; - -/* - * BIOS auto configuration - */ -static int alc268_parse_auto_config(struct hda_codec *codec) -{ - static const hda_nid_t alc268_ssids[] = { 0x15, 0x1b, 0x14, 0 }; - struct alc_spec *spec = codec->spec; - int err = alc_parse_auto_config(codec, NULL, alc268_ssids); - if (err > 0) { - if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d) { - add_mixer(spec, alc268_beep_mixer); - add_verb(spec, alc268_beep_init_verbs); - } - } - return err; -} - -/* - */ -static int patch_alc268(struct hda_codec *codec) -{ - struct alc_spec *spec; - int i, has_beep, err; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - - /* ALC268 has no aa-loopback mixer */ - - /* automatic parse from the BIOS config */ - err = alc268_parse_auto_config(codec); - if (err < 0) - goto error; - - has_beep = 0; - for (i = 0; i < spec->num_mixers; i++) { - if (spec->mixers[i] == alc268_beep_mixer) { - has_beep = 1; - break; - } - } - - if (has_beep) { - err = snd_hda_attach_beep_device(codec, 0x1); - if (err < 0) - goto error; - if (!query_amp_caps(codec, 0x1d, HDA_INPUT)) - /* override the amp caps for beep generator */ - snd_hda_override_amp_caps(codec, 0x1d, HDA_INPUT, - (0x0c << AC_AMPCAP_OFFSET_SHIFT) | - (0x0c << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x07 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (0 << AC_AMPCAP_MUTE_SHIFT)); - } - - codec->patch_ops = alc_patch_ops; - spec->shutup = alc_eapd_shutup; - - return 0; - - error: - alc_free(codec); - return err; -} - -/* - * ALC269 - */ -static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 8, - .rates = SNDRV_PCM_RATE_44100, /* fixed rate */ - /* NID is set in alc_build_pcms */ - .ops = { - .open = alc_playback_pcm_open, - .prepare = alc_playback_pcm_prepare, - .cleanup = alc_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream alc269_44k_pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_44100, /* fixed rate */ - /* NID is set in alc_build_pcms */ -}; - -/* different alc269-variants */ -enum { - ALC269_TYPE_ALC269VA, - ALC269_TYPE_ALC269VB, - ALC269_TYPE_ALC269VC, -}; - -/* - * BIOS auto configuration - */ -static int alc269_parse_auto_config(struct hda_codec *codec) -{ - static const hda_nid_t alc269_ignore[] = { 0x1d, 0 }; - static const hda_nid_t alc269_ssids[] = { 0, 0x1b, 0x14, 0x21 }; - static const hda_nid_t alc269va_ssids[] = { 0x15, 0x1b, 0x14, 0 }; - struct alc_spec *spec = codec->spec; - const hda_nid_t *ssids = spec->codec_variant == ALC269_TYPE_ALC269VA ? - alc269va_ssids : alc269_ssids; - - return alc_parse_auto_config(codec, alc269_ignore, ssids); -} - -static void alc269_toggle_power_output(struct hda_codec *codec, int power_up) -{ - int val = alc_read_coef_idx(codec, 0x04); - if (power_up) - val |= 1 << 11; - else - val &= ~(1 << 11); - alc_write_coef_idx(codec, 0x04, val); -} - -static void alc269_shutup(struct hda_codec *codec) -{ - if ((alc_get_coef0(codec) & 0x00ff) == 0x017) - alc269_toggle_power_output(codec, 0); - if ((alc_get_coef0(codec) & 0x00ff) == 0x018) { - alc269_toggle_power_output(codec, 0); - msleep(150); - } -} - -#ifdef CONFIG_PM -static int alc269_resume(struct hda_codec *codec) -{ - if ((alc_get_coef0(codec) & 0x00ff) == 0x018) { - alc269_toggle_power_output(codec, 0); - msleep(150); - } - - codec->patch_ops.init(codec); - - if ((alc_get_coef0(codec) & 0x00ff) == 0x017) { - alc269_toggle_power_output(codec, 1); - msleep(200); - } - - if ((alc_get_coef0(codec) & 0x00ff) == 0x018) - alc269_toggle_power_output(codec, 1); - - snd_hda_codec_resume_amp(codec); - snd_hda_codec_resume_cache(codec); - hda_call_check_power_status(codec, 0x01); - return 0; -} -#endif /* CONFIG_PM */ - -static void alc269_fixup_hweq(struct hda_codec *codec, - const struct alc_fixup *fix, int action) -{ - int coef; - - if (action != ALC_FIXUP_ACT_INIT) - return; - coef = alc_read_coef_idx(codec, 0x1e); - alc_write_coef_idx(codec, 0x1e, coef | 0x80); -} - -static void alc271_fixup_dmic(struct hda_codec *codec, - const struct alc_fixup *fix, int action) -{ - static const struct hda_verb verbs[] = { - {0x20, AC_VERB_SET_COEF_INDEX, 0x0d}, - {0x20, AC_VERB_SET_PROC_COEF, 0x4000}, - {} - }; - unsigned int cfg; - - if (strcmp(codec->chip_name, "ALC271X")) - return; - cfg = snd_hda_codec_get_pincfg(codec, 0x12); - if (get_defcfg_connect(cfg) == AC_JACK_PORT_FIXED) - snd_hda_sequence_write(codec, verbs); -} - -static void alc269_fixup_pcm_44k(struct hda_codec *codec, - const struct alc_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - if (action != ALC_FIXUP_ACT_PROBE) - return; - - /* Due to a hardware problem on Lenovo Ideadpad, we need to - * fix the sample rate of analog I/O to 44.1kHz - */ - spec->stream_analog_playback = &alc269_44k_pcm_analog_playback; - spec->stream_analog_capture = &alc269_44k_pcm_analog_capture; -} - -static void alc269_fixup_stereo_dmic(struct hda_codec *codec, - const struct alc_fixup *fix, int action) -{ - int coef; - - if (action != ALC_FIXUP_ACT_INIT) - return; - /* The digital-mic unit sends PDM (differential signal) instead of - * the standard PCM, thus you can't record a valid mono stream as is. - * Below is a workaround specific to ALC269 to control the dmic - * signal source as mono. - */ - coef = alc_read_coef_idx(codec, 0x07); - alc_write_coef_idx(codec, 0x07, coef | 0x80); -} - -static void alc269_quanta_automute(struct hda_codec *codec) -{ - update_outputs(codec); - - 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_fixup_quanta_mute(struct hda_codec *codec, - const struct alc_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - if (action != ALC_FIXUP_ACT_PROBE) - return; - spec->automute_hook = alc269_quanta_automute; -} - -/* update mute-LED according to the speaker mute state via mic2 VREF pin */ -static void alc269_fixup_mic2_mute_hook(void *private_data, int enabled) -{ - struct hda_codec *codec = private_data; - unsigned int pinval = enabled ? 0x20 : 0x24; - snd_hda_codec_update_cache(codec, 0x19, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - pinval); -} - -static void alc269_fixup_mic2_mute(struct hda_codec *codec, - const struct alc_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - switch (action) { - case ALC_FIXUP_ACT_BUILD: - spec->vmaster_mute.hook = alc269_fixup_mic2_mute_hook; - snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true); - /* fallthru */ - case ALC_FIXUP_ACT_INIT: - snd_hda_sync_vmaster_hook(&spec->vmaster_mute); - break; - } -} - -enum { - ALC269_FIXUP_SONY_VAIO, - ALC275_FIXUP_SONY_VAIO_GPIO2, - ALC269_FIXUP_DELL_M101Z, - ALC269_FIXUP_SKU_IGNORE, - ALC269_FIXUP_ASUS_G73JW, - ALC269_FIXUP_LENOVO_EAPD, - ALC275_FIXUP_SONY_HWEQ, - ALC271_FIXUP_DMIC, - ALC269_FIXUP_PCM_44K, - ALC269_FIXUP_STEREO_DMIC, - ALC269_FIXUP_QUANTA_MUTE, - ALC269_FIXUP_LIFEBOOK, - ALC269_FIXUP_AMIC, - ALC269_FIXUP_DMIC, - ALC269VB_FIXUP_AMIC, - ALC269VB_FIXUP_DMIC, - ALC269_FIXUP_MIC2_MUTE_LED, -}; - -static const struct alc_fixup alc269_fixups[] = { - [ALC269_FIXUP_SONY_VAIO] = { - .type = ALC_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREFGRD}, - {} - } - }, - [ALC275_FIXUP_SONY_VAIO_GPIO2] = { - .type = ALC_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - {0x01, AC_VERB_SET_GPIO_MASK, 0x04}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x04}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x00}, - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_SONY_VAIO - }, - [ALC269_FIXUP_DELL_M101Z] = { - .type = ALC_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* Enables internal speaker */ - {0x20, AC_VERB_SET_COEF_INDEX, 13}, - {0x20, AC_VERB_SET_PROC_COEF, 0x4040}, - {} - } - }, - [ALC269_FIXUP_SKU_IGNORE] = { - .type = ALC_FIXUP_SKU, - .v.sku = ALC_FIXUP_SKU_IGNORE, - }, - [ALC269_FIXUP_ASUS_G73JW] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x17, 0x99130111 }, /* subwoofer */ - { } - } - }, - [ALC269_FIXUP_LENOVO_EAPD] = { - .type = ALC_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - {0x14, AC_VERB_SET_EAPD_BTLENABLE, 0}, - {} - } - }, - [ALC275_FIXUP_SONY_HWEQ] = { - .type = ALC_FIXUP_FUNC, - .v.func = alc269_fixup_hweq, - .chained = true, - .chain_id = ALC275_FIXUP_SONY_VAIO_GPIO2 - }, - [ALC271_FIXUP_DMIC] = { - .type = ALC_FIXUP_FUNC, - .v.func = alc271_fixup_dmic, - }, - [ALC269_FIXUP_PCM_44K] = { - .type = ALC_FIXUP_FUNC, - .v.func = alc269_fixup_pcm_44k, - }, - [ALC269_FIXUP_STEREO_DMIC] = { - .type = ALC_FIXUP_FUNC, - .v.func = alc269_fixup_stereo_dmic, - }, - [ALC269_FIXUP_QUANTA_MUTE] = { - .type = ALC_FIXUP_FUNC, - .v.func = alc269_fixup_quanta_mute, - }, - [ALC269_FIXUP_LIFEBOOK] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x1a, 0x2101103f }, /* dock line-out */ - { 0x1b, 0x23a11040 }, /* dock mic-in */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_QUANTA_MUTE - }, - [ALC269_FIXUP_AMIC] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x15, 0x0121401f }, /* HP out */ - { 0x18, 0x01a19c20 }, /* mic */ - { 0x19, 0x99a3092f }, /* int-mic */ - { } - }, - }, - [ALC269_FIXUP_DMIC] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x12, 0x99a3092f }, /* int-mic */ - { 0x14, 0x99130110 }, /* speaker */ - { 0x15, 0x0121401f }, /* HP out */ - { 0x18, 0x01a19c20 }, /* mic */ - { } - }, - }, - [ALC269VB_FIXUP_AMIC] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x18, 0x01a19c20 }, /* mic */ - { 0x19, 0x99a3092f }, /* int-mic */ - { 0x21, 0x0121401f }, /* HP out */ - { } - }, - }, - [ALC269VB_FIXUP_DMIC] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x12, 0x99a3092f }, /* int-mic */ - { 0x14, 0x99130110 }, /* speaker */ - { 0x18, 0x01a19c20 }, /* mic */ - { 0x21, 0x0121401f }, /* HP out */ - { } - }, - }, - [ALC269_FIXUP_MIC2_MUTE_LED] = { - .type = ALC_FIXUP_FUNC, - .v.func = alc269_fixup_mic2_mute, - }, -}; - -static const struct snd_pci_quirk alc269_fixup_tbl[] = { - SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_MIC2_MUTE_LED), - SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_DMIC), - SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW), - SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC), - SND_PCI_QUIRK(0x1043, 0x831a, "ASUS P901", ALC269_FIXUP_STEREO_DMIC), - SND_PCI_QUIRK(0x1043, 0x834a, "ASUS S101", ALC269_FIXUP_STEREO_DMIC), - SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC), - SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC), - SND_PCI_QUIRK(0x104d, 0x9073, "Sony VAIO", ALC275_FIXUP_SONY_VAIO_GPIO2), - SND_PCI_QUIRK(0x104d, 0x907b, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), - SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), - SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO), - SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z), - SND_PCI_QUIRK_VENDOR(0x1025, "Acer Aspire", ALC271_FIXUP_DMIC), - SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook", ALC269_FIXUP_LIFEBOOK), - SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE), - SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE), - SND_PCI_QUIRK(0x17aa, 0x21b8, "Thinkpad Edge 14", ALC269_FIXUP_SKU_IGNORE), - SND_PCI_QUIRK(0x17aa, 0x21ca, "Thinkpad L412", ALC269_FIXUP_SKU_IGNORE), - SND_PCI_QUIRK(0x17aa, 0x21e9, "Thinkpad Edge 15", ALC269_FIXUP_SKU_IGNORE), - SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_QUANTA_MUTE), - SND_PCI_QUIRK(0x17aa, 0x3bf8, "Lenovo Ideapd", ALC269_FIXUP_PCM_44K), - SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD), - -#if 0 - /* Below is a quirk table taken from the old code. - * Basically the device should work as is without the fixup table. - * If BIOS doesn't give a proper info, enable the corresponding - * fixup entry. - */ - SND_PCI_QUIRK(0x1043, 0x8330, "ASUS Eeepc P703 P900A", - ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1013, "ASUS N61Da", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1143, "ASUS B53f", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1133, "ASUS UJ20ft", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1183, "ASUS K72DR", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x11b3, "ASUS K52DR", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x11e3, "ASUS U33Jc", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1273, "ASUS UL80Jt", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1283, "ASUS U53Jc", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS N82JV", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x12d3, "ASUS N61Jv", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x13a3, "ASUS UL30Vt", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1373, "ASUS G73JX", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1383, "ASUS UJ30Jc", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x13d3, "ASUS N61JA", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1413, "ASUS UL50", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1443, "ASUS UL30", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1453, "ASUS M60Jv", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1483, "ASUS UL80", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x14f3, "ASUS F83Vf", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x14e3, "ASUS UL20", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1513, "ASUS UX30", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1593, "ASUS N51Vn", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x15a3, "ASUS N60Jv", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x15b3, "ASUS N60Dp", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x15c3, "ASUS N70De", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x15e3, "ASUS F83T", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1643, "ASUS M60J", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1653, "ASUS U50", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1693, "ASUS F50N", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS F5Q", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1723, "ASUS P80", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1743, "ASUS U80", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1773, "ASUS U20A", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1883, "ASUS F81Se", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x152d, 0x1778, "Quanta ON1", ALC269_FIXUP_DMIC), - SND_PCI_QUIRK(0x17aa, 0x3be9, "Quanta Wistron", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x17ff, 0x059a, "Quanta EL3", ALC269_FIXUP_DMIC), - SND_PCI_QUIRK(0x17ff, 0x059b, "Quanta JR1", ALC269_FIXUP_DMIC), -#endif - {} -}; - -static const struct alc_model_fixup alc269_fixup_models[] = { - {.id = ALC269_FIXUP_AMIC, .name = "laptop-amic"}, - {.id = ALC269_FIXUP_DMIC, .name = "laptop-dmic"}, - {} -}; - - -static void alc269_fill_coef(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int val; - - if (spec->codec_variant != ALC269_TYPE_ALC269VB) - return; - - if ((alc_get_coef0(codec) & 0x00ff) < 0x015) { - alc_write_coef_idx(codec, 0xf, 0x960b); - alc_write_coef_idx(codec, 0xe, 0x8817); - } - - if ((alc_get_coef0(codec) & 0x00ff) == 0x016) { - alc_write_coef_idx(codec, 0xf, 0x960b); - alc_write_coef_idx(codec, 0xe, 0x8814); - } - - if ((alc_get_coef0(codec) & 0x00ff) == 0x017) { - val = alc_read_coef_idx(codec, 0x04); - /* Power up output pin */ - alc_write_coef_idx(codec, 0x04, val | (1<<11)); - } - - if ((alc_get_coef0(codec) & 0x00ff) == 0x018) { - val = alc_read_coef_idx(codec, 0xd); - if ((val & 0x0c00) >> 10 != 0x1) { - /* Capless ramp up clock control */ - alc_write_coef_idx(codec, 0xd, val | (1<<10)); - } - val = alc_read_coef_idx(codec, 0x17); - if ((val & 0x01c0) >> 6 != 0x4) { - /* Class D power on reset */ - alc_write_coef_idx(codec, 0x17, val | (1<<7)); - } - } - - val = alc_read_coef_idx(codec, 0xd); /* Class D */ - alc_write_coef_idx(codec, 0xd, val | (1<<14)); - - val = alc_read_coef_idx(codec, 0x4); /* HP */ - alc_write_coef_idx(codec, 0x4, val | (1<<11)); -} - -/* - */ -static int patch_alc269(struct hda_codec *codec) -{ - struct alc_spec *spec; - int err = 0; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - - spec->mixer_nid = 0x0b; - - err = alc_codec_rename_from_preset(codec); - if (err < 0) - goto error; - - if (codec->vendor_id == 0x10ec0269) { - spec->codec_variant = ALC269_TYPE_ALC269VA; - switch (alc_get_coef0(codec) & 0x00f0) { - case 0x0010: - if (codec->bus->pci->subsystem_vendor == 0x1025 && - spec->cdefine.platform_type == 1) - err = alc_codec_rename(codec, "ALC271X"); - spec->codec_variant = ALC269_TYPE_ALC269VB; - break; - case 0x0020: - if (codec->bus->pci->subsystem_vendor == 0x17aa && - codec->bus->pci->subsystem_device == 0x21f3) - err = alc_codec_rename(codec, "ALC3202"); - spec->codec_variant = ALC269_TYPE_ALC269VC; - break; - default: - alc_fix_pll_init(codec, 0x20, 0x04, 15); - } - if (err < 0) - goto error; - spec->init_hook = alc269_fill_coef; - alc269_fill_coef(codec); - } - - alc_pick_fixup(codec, alc269_fixup_models, - alc269_fixup_tbl, alc269_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); - - alc_auto_parse_customize_define(codec); - - /* automatic parse from the BIOS config */ - err = alc269_parse_auto_config(codec); - if (err < 0) - goto error; - - if (!spec->no_analog && has_cdefine_beep(codec)) { - err = snd_hda_attach_beep_device(codec, 0x1); - if (err < 0) - goto error; - set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT); - } - - codec->patch_ops = alc_patch_ops; -#ifdef CONFIG_PM - codec->patch_ops.resume = alc269_resume; -#endif - spec->shutup = alc269_shutup; - - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); - - return 0; - - error: - alc_free(codec); - return err; -} - -/* - * ALC861 - */ - -static int alc861_parse_auto_config(struct hda_codec *codec) -{ - static const hda_nid_t alc861_ignore[] = { 0x1d, 0 }; - static const hda_nid_t alc861_ssids[] = { 0x0e, 0x0f, 0x0b, 0 }; - return alc_parse_auto_config(codec, alc861_ignore, alc861_ssids); -} - -/* Pin config fixes */ -enum { - ALC861_FIXUP_FSC_AMILO_PI1505, - ALC861_FIXUP_AMP_VREF_0F, - ALC861_FIXUP_NO_JACK_DETECT, - ALC861_FIXUP_ASUS_A6RP, -}; - -/* On some laptops, VREF of pin 0x0f is abused for controlling the main amp */ -static void alc861_fixup_asus_amp_vref_0f(struct hda_codec *codec, - const struct alc_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - unsigned int val; - - if (action != ALC_FIXUP_ACT_INIT) - return; - val = snd_hda_codec_read(codec, 0x0f, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - if (!(val & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN))) - val |= AC_PINCTL_IN_EN; - val |= AC_PINCTL_VREF_50; - snd_hda_codec_write(codec, 0x0f, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, val); - spec->keep_vref_in_automute = 1; -} - -/* suppress the jack-detection */ -static void alc_fixup_no_jack_detect(struct hda_codec *codec, - const struct alc_fixup *fix, int action) -{ - if (action == ALC_FIXUP_ACT_PRE_PROBE) - codec->no_jack_detect = 1; -} - -static const struct alc_fixup alc861_fixups[] = { - [ALC861_FIXUP_FSC_AMILO_PI1505] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x0b, 0x0221101f }, /* HP */ - { 0x0f, 0x90170310 }, /* speaker */ - { } - } - }, - [ALC861_FIXUP_AMP_VREF_0F] = { - .type = ALC_FIXUP_FUNC, - .v.func = alc861_fixup_asus_amp_vref_0f, - }, - [ALC861_FIXUP_NO_JACK_DETECT] = { - .type = ALC_FIXUP_FUNC, - .v.func = alc_fixup_no_jack_detect, - }, - [ALC861_FIXUP_ASUS_A6RP] = { - .type = ALC_FIXUP_FUNC, - .v.func = alc861_fixup_asus_amp_vref_0f, - .chained = true, - .chain_id = ALC861_FIXUP_NO_JACK_DETECT, - } -}; - -static const struct snd_pci_quirk alc861_fixup_tbl[] = { - SND_PCI_QUIRK(0x1043, 0x1393, "ASUS A6Rp", ALC861_FIXUP_ASUS_A6RP), - SND_PCI_QUIRK_VENDOR(0x1043, "ASUS laptop", ALC861_FIXUP_AMP_VREF_0F), - SND_PCI_QUIRK(0x1462, 0x7254, "HP DX2200", ALC861_FIXUP_NO_JACK_DETECT), - SND_PCI_QUIRK(0x1584, 0x2b01, "Haier W18", ALC861_FIXUP_AMP_VREF_0F), - SND_PCI_QUIRK(0x1584, 0x0000, "Uniwill ECS M31EI", ALC861_FIXUP_AMP_VREF_0F), - SND_PCI_QUIRK(0x1734, 0x10c7, "FSC Amilo Pi1505", ALC861_FIXUP_FSC_AMILO_PI1505), - {} -}; - -/* - */ -static int patch_alc861(struct hda_codec *codec) -{ - struct alc_spec *spec; - int err; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - - spec->mixer_nid = 0x15; - - alc_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); - - /* automatic parse from the BIOS config */ - err = alc861_parse_auto_config(codec); - if (err < 0) - goto error; - - if (!spec->no_analog) { - err = snd_hda_attach_beep_device(codec, 0x23); - if (err < 0) - goto error; - set_beep_amp(spec, 0x23, 0, HDA_OUTPUT); - } - - codec->patch_ops = alc_patch_ops; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->power_hook = alc_power_eapd; -#endif - - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); - - return 0; - - error: - alc_free(codec); - return err; -} - -/* - * ALC861-VD support - * - * Based on ALC882 - * - * In addition, an independent DAC - */ -static int alc861vd_parse_auto_config(struct hda_codec *codec) -{ - static const hda_nid_t alc861vd_ignore[] = { 0x1d, 0 }; - static const hda_nid_t alc861vd_ssids[] = { 0x15, 0x1b, 0x14, 0 }; - return alc_parse_auto_config(codec, alc861vd_ignore, alc861vd_ssids); -} - -enum { - ALC660VD_FIX_ASUS_GPIO1, - ALC861VD_FIX_DALLAS, -}; - -/* exclude VREF80 */ -static void alc861vd_fixup_dallas(struct hda_codec *codec, - const struct alc_fixup *fix, int action) -{ - if (action == ALC_FIXUP_ACT_PRE_PROBE) { - snd_hda_override_pin_caps(codec, 0x18, 0x00001714); - snd_hda_override_pin_caps(codec, 0x19, 0x0000171c); - } -} - -static const struct alc_fixup alc861vd_fixups[] = { - [ALC660VD_FIX_ASUS_GPIO1] = { - .type = ALC_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* reset GPIO1 */ - {0x01, AC_VERB_SET_GPIO_MASK, 0x03}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x01}, - { } - } - }, - [ALC861VD_FIX_DALLAS] = { - .type = ALC_FIXUP_FUNC, - .v.func = alc861vd_fixup_dallas, - }, -}; - -static const struct snd_pci_quirk alc861vd_fixup_tbl[] = { - SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_FIX_DALLAS), - SND_PCI_QUIRK(0x1043, 0x1339, "ASUS A7-K", ALC660VD_FIX_ASUS_GPIO1), - SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba L30-149", ALC861VD_FIX_DALLAS), - {} -}; - -static const struct hda_verb alc660vd_eapd_verbs[] = { - {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, - {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, - { } -}; - -/* - */ -static int patch_alc861vd(struct hda_codec *codec) -{ - struct alc_spec *spec; - int err; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - - spec->mixer_nid = 0x0b; - - alc_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); - - /* automatic parse from the BIOS config */ - err = alc861vd_parse_auto_config(codec); - if (err < 0) - goto error; - - if (codec->vendor_id == 0x10ec0660) { - /* always turn on EAPD */ - add_verb(spec, alc660vd_eapd_verbs); - } - - if (!spec->no_analog) { - err = snd_hda_attach_beep_device(codec, 0x23); - if (err < 0) - goto error; - set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); - } - - codec->patch_ops = alc_patch_ops; - - spec->shutup = alc_eapd_shutup; - - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); - - return 0; - - error: - alc_free(codec); - return err; -} - -/* - * ALC662 support - * - * ALC662 is almost identical with ALC880 but has cleaner and more flexible - * configuration. Each pin widget can choose any input DACs and a mixer. - * Each ADC is connected from a mixer of all inputs. This makes possible - * 6-channel independent captures. - * - * In addition, an independent DAC for the multi-playback (not used in this - * driver yet). - */ - -/* - * BIOS auto configuration - */ - -static int alc662_parse_auto_config(struct hda_codec *codec) -{ - static const hda_nid_t alc662_ignore[] = { 0x1d, 0 }; - static const hda_nid_t alc663_ssids[] = { 0x15, 0x1b, 0x14, 0x21 }; - static const hda_nid_t alc662_ssids[] = { 0x15, 0x1b, 0x14, 0 }; - const hda_nid_t *ssids; - - if (codec->vendor_id == 0x10ec0272 || codec->vendor_id == 0x10ec0663 || - codec->vendor_id == 0x10ec0665 || codec->vendor_id == 0x10ec0670) - ssids = alc663_ssids; - else - ssids = alc662_ssids; - return alc_parse_auto_config(codec, alc662_ignore, ssids); -} - -static void alc272_fixup_mario(struct hda_codec *codec, - const struct alc_fixup *fix, int action) -{ - if (action != ALC_FIXUP_ACT_PROBE) - return; - if (snd_hda_override_amp_caps(codec, 0x2, HDA_OUTPUT, - (0x3b << AC_AMPCAP_OFFSET_SHIFT) | - (0x3b << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x03 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (0 << AC_AMPCAP_MUTE_SHIFT))) - printk(KERN_WARNING - "hda_codec: failed to override amp caps for NID 0x2\n"); -} - -enum { - ALC662_FIXUP_ASPIRE, - ALC662_FIXUP_IDEAPAD, - ALC272_FIXUP_MARIO, - ALC662_FIXUP_CZC_P10T, - ALC662_FIXUP_SKU_IGNORE, - ALC662_FIXUP_HP_RP5800, - ALC662_FIXUP_ASUS_MODE1, - ALC662_FIXUP_ASUS_MODE2, - ALC662_FIXUP_ASUS_MODE3, - ALC662_FIXUP_ASUS_MODE4, - ALC662_FIXUP_ASUS_MODE5, - ALC662_FIXUP_ASUS_MODE6, - ALC662_FIXUP_ASUS_MODE7, - ALC662_FIXUP_ASUS_MODE8, - ALC662_FIXUP_NO_JACK_DETECT, - ALC662_FIXUP_ZOTAC_Z68, -}; - -static const struct alc_fixup alc662_fixups[] = { - [ALC662_FIXUP_ASPIRE] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x15, 0x99130112 }, /* subwoofer */ - { } - } - }, - [ALC662_FIXUP_IDEAPAD] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x17, 0x99130112 }, /* subwoofer */ - { } - } - }, - [ALC272_FIXUP_MARIO] = { - .type = ALC_FIXUP_FUNC, - .v.func = alc272_fixup_mario, - }, - [ALC662_FIXUP_CZC_P10T] = { - .type = ALC_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - {0x14, AC_VERB_SET_EAPD_BTLENABLE, 0}, - {} - } - }, - [ALC662_FIXUP_SKU_IGNORE] = { - .type = ALC_FIXUP_SKU, - .v.sku = ALC_FIXUP_SKU_IGNORE, - }, - [ALC662_FIXUP_HP_RP5800] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x14, 0x0221201f }, /* HP out */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE - }, - [ALC662_FIXUP_ASUS_MODE1] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x18, 0x01a19c20 }, /* mic */ - { 0x19, 0x99a3092f }, /* int-mic */ - { 0x21, 0x0121401f }, /* HP out */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE - }, - [ALC662_FIXUP_ASUS_MODE2] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x18, 0x01a19820 }, /* mic */ - { 0x19, 0x99a3092f }, /* int-mic */ - { 0x1b, 0x0121401f }, /* HP out */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE - }, - [ALC662_FIXUP_ASUS_MODE3] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x15, 0x0121441f }, /* HP */ - { 0x18, 0x01a19840 }, /* mic */ - { 0x19, 0x99a3094f }, /* int-mic */ - { 0x21, 0x01211420 }, /* HP2 */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE - }, - [ALC662_FIXUP_ASUS_MODE4] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x16, 0x99130111 }, /* speaker */ - { 0x18, 0x01a19840 }, /* mic */ - { 0x19, 0x99a3094f }, /* int-mic */ - { 0x21, 0x0121441f }, /* HP */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE - }, - [ALC662_FIXUP_ASUS_MODE5] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x15, 0x0121441f }, /* HP */ - { 0x16, 0x99130111 }, /* speaker */ - { 0x18, 0x01a19840 }, /* mic */ - { 0x19, 0x99a3094f }, /* int-mic */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE - }, - [ALC662_FIXUP_ASUS_MODE6] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x15, 0x01211420 }, /* HP2 */ - { 0x18, 0x01a19840 }, /* mic */ - { 0x19, 0x99a3094f }, /* int-mic */ - { 0x1b, 0x0121441f }, /* HP */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE - }, - [ALC662_FIXUP_ASUS_MODE7] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x17, 0x99130111 }, /* speaker */ - { 0x18, 0x01a19840 }, /* mic */ - { 0x19, 0x99a3094f }, /* int-mic */ - { 0x1b, 0x01214020 }, /* HP */ - { 0x21, 0x0121401f }, /* HP */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE - }, - [ALC662_FIXUP_ASUS_MODE8] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x12, 0x99a30970 }, /* int-mic */ - { 0x15, 0x01214020 }, /* HP */ - { 0x17, 0x99130111 }, /* speaker */ - { 0x18, 0x01a19840 }, /* mic */ - { 0x21, 0x0121401f }, /* HP */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE - }, - [ALC662_FIXUP_NO_JACK_DETECT] = { - .type = ALC_FIXUP_FUNC, - .v.func = alc_fixup_no_jack_detect, - }, - [ALC662_FIXUP_ZOTAC_Z68] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x1b, 0x02214020 }, /* Front HP */ - { } - } - }, -}; - -static const struct snd_pci_quirk alc662_fixup_tbl[] = { - SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE), - SND_PCI_QUIRK(0x1025, 0x031c, "Gateway NV79", ALC662_FIXUP_SKU_IGNORE), - SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE), - SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800), - SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT), - SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD), - SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD), - SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD), - SND_PCI_QUIRK(0x19da, 0xa130, "Zotac Z68", ALC662_FIXUP_ZOTAC_Z68), - SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T), - -#if 0 - /* Below is a quirk table taken from the old code. - * Basically the device should work as is without the fixup table. - * If BIOS doesn't give a proper info, enable the corresponding - * fixup entry. - */ - SND_PCI_QUIRK(0x1043, 0x1000, "ASUS N50Vm", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1092, "ASUS NB", ALC662_FIXUP_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x1173, "ASUS K73Jn", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x11c3, "ASUS M70V", ALC662_FIXUP_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x11d3, "ASUS NB", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x11f3, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1203, "ASUS NB", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1303, "ASUS G60J", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1333, "ASUS G60Jx", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1339, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x13e3, "ASUS N71JA", ALC662_FIXUP_ASUS_MODE7), - SND_PCI_QUIRK(0x1043, 0x1463, "ASUS N71", ALC662_FIXUP_ASUS_MODE7), - SND_PCI_QUIRK(0x1043, 0x14d3, "ASUS G72", ALC662_FIXUP_ASUS_MODE8), - SND_PCI_QUIRK(0x1043, 0x1563, "ASUS N90", ALC662_FIXUP_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x15d3, "ASUS N50SF F50SF", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x16c3, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x16f3, "ASUS K40C K50C", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1733, "ASUS N81De", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1753, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1763, "ASUS NB", ALC662_FIXUP_ASUS_MODE6), - SND_PCI_QUIRK(0x1043, 0x1765, "ASUS NB", ALC662_FIXUP_ASUS_MODE6), - SND_PCI_QUIRK(0x1043, 0x1783, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1793, "ASUS F50GX", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x17b3, "ASUS F70SL", ALC662_FIXUP_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x17f3, "ASUS X58LE", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1813, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1823, "ASUS NB", ALC662_FIXUP_ASUS_MODE5), - SND_PCI_QUIRK(0x1043, 0x1833, "ASUS NB", ALC662_FIXUP_ASUS_MODE6), - SND_PCI_QUIRK(0x1043, 0x1843, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1853, "ASUS F50Z", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1864, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1876, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1893, "ASUS M50Vm", ALC662_FIXUP_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x1894, "ASUS X55", ALC662_FIXUP_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x18b3, "ASUS N80Vc", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x18c3, "ASUS VX5", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x18d3, "ASUS N81Te", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x18f3, "ASUS N505Tp", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1903, "ASUS F5GL", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1913, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1933, "ASUS F80Q", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1943, "ASUS Vx3V", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1953, "ASUS NB", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71C", ALC662_FIXUP_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x1983, "ASUS N5051A", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1993, "ASUS N20", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS F7Z", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x19c3, "ASUS F5Z/F6x", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x19e3, "ASUS NB", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x19f3, "ASUS NB", ALC662_FIXUP_ASUS_MODE4), -#endif - {} -}; - -static const struct alc_model_fixup alc662_fixup_models[] = { - {.id = ALC272_FIXUP_MARIO, .name = "mario"}, - {.id = ALC662_FIXUP_ASUS_MODE1, .name = "asus-mode1"}, - {.id = ALC662_FIXUP_ASUS_MODE2, .name = "asus-mode2"}, - {.id = ALC662_FIXUP_ASUS_MODE3, .name = "asus-mode3"}, - {.id = ALC662_FIXUP_ASUS_MODE4, .name = "asus-mode4"}, - {.id = ALC662_FIXUP_ASUS_MODE5, .name = "asus-mode5"}, - {.id = ALC662_FIXUP_ASUS_MODE6, .name = "asus-mode6"}, - {.id = ALC662_FIXUP_ASUS_MODE7, .name = "asus-mode7"}, - {.id = ALC662_FIXUP_ASUS_MODE8, .name = "asus-mode8"}, - {} -}; - - -/* - */ -static int patch_alc662(struct hda_codec *codec) -{ - struct alc_spec *spec; - int err = 0; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return -ENOMEM; - - codec->spec = spec; - - spec->mixer_nid = 0x0b; - - /* handle multiple HPs as is */ - spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP; - - alc_fix_pll_init(codec, 0x20, 0x04, 15); - - err = alc_codec_rename_from_preset(codec); - if (err < 0) - goto error; - - if ((alc_get_coef0(codec) & (1 << 14)) && - codec->bus->pci->subsystem_vendor == 0x1025 && - spec->cdefine.platform_type == 1) { - if (alc_codec_rename(codec, "ALC272X") < 0) - goto error; - } - - alc_pick_fixup(codec, alc662_fixup_models, - alc662_fixup_tbl, alc662_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); - - alc_auto_parse_customize_define(codec); - - /* automatic parse from the BIOS config */ - err = alc662_parse_auto_config(codec); - if (err < 0) - goto error; - - if (!spec->no_analog && has_cdefine_beep(codec)) { - err = snd_hda_attach_beep_device(codec, 0x1); - if (err < 0) - goto error; - switch (codec->vendor_id) { - case 0x10ec0662: - set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); - break; - case 0x10ec0272: - case 0x10ec0663: - case 0x10ec0665: - set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT); - break; - case 0x10ec0273: - set_beep_amp(spec, 0x0b, 0x03, HDA_INPUT); - break; - } - } - - codec->patch_ops = alc_patch_ops; - spec->shutup = alc_eapd_shutup; - - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); - - return 0; - - error: - alc_free(codec); - return err; -} - -/* - * ALC680 support - */ - -static int alc680_parse_auto_config(struct hda_codec *codec) -{ - return alc_parse_auto_config(codec, NULL, NULL); -} - -/* - */ -static int patch_alc680(struct hda_codec *codec) -{ - struct alc_spec *spec; - int err; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - - /* ALC680 has no aa-loopback mixer */ - - /* automatic parse from the BIOS config */ - err = alc680_parse_auto_config(codec); - if (err < 0) { - alc_free(codec); - return err; - } - - codec->patch_ops = alc_patch_ops; - - return 0; -} - -/* - * patch entries - */ -static const struct hda_codec_preset snd_hda_preset_realtek[] = { - { .id = 0x10ec0221, .name = "ALC221", .patch = patch_alc269 }, - { .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 = 0x10ec0270, .name = "ALC270", .patch = patch_alc269 }, - { .id = 0x10ec0272, .name = "ALC272", .patch = patch_alc662 }, - { .id = 0x10ec0275, .name = "ALC275", .patch = patch_alc269 }, - { .id = 0x10ec0276, .name = "ALC276", .patch = patch_alc269 }, - { .id = 0x10ec0280, .name = "ALC280", .patch = patch_alc269 }, - { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660", - .patch = patch_alc861 }, - { .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd }, - { .id = 0x10ec0861, .name = "ALC861", .patch = patch_alc861 }, - { .id = 0x10ec0862, .name = "ALC861-VD", .patch = patch_alc861vd }, - { .id = 0x10ec0662, .rev = 0x100002, .name = "ALC662 rev2", - .patch = patch_alc882 }, - { .id = 0x10ec0662, .rev = 0x100101, .name = "ALC662 rev1", - .patch = patch_alc662 }, - { .id = 0x10ec0662, .rev = 0x100300, .name = "ALC662 rev3", - .patch = patch_alc662 }, - { .id = 0x10ec0663, .name = "ALC663", .patch = patch_alc662 }, - { .id = 0x10ec0665, .name = "ALC665", .patch = patch_alc662 }, - { .id = 0x10ec0670, .name = "ALC670", .patch = patch_alc662 }, - { .id = 0x10ec0680, .name = "ALC680", .patch = patch_alc680 }, - { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 }, - { .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 }, - { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc882 }, - { .id = 0x10ec0885, .rev = 0x100101, .name = "ALC889A", - .patch = patch_alc882 }, - { .id = 0x10ec0885, .rev = 0x100103, .name = "ALC889A", - .patch = patch_alc882 }, - { .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 }, - { .id = 0x10ec0887, .name = "ALC887", .patch = patch_alc882 }, - { .id = 0x10ec0888, .rev = 0x100101, .name = "ALC1200", - .patch = patch_alc882 }, - { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc882 }, - { .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc882 }, - { .id = 0x10ec0892, .name = "ALC892", .patch = patch_alc662 }, - { .id = 0x10ec0899, .name = "ALC898", .patch = patch_alc882 }, - {} /* 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/ANDROID_3.4.5/sound/pci/hda/patch_si3054.c b/ANDROID_3.4.5/sound/pci/hda/patch_si3054.c deleted file mode 100644 index 6679a509..00000000 --- a/ANDROID_3.4.5/sound/pci/hda/patch_si3054.c +++ /dev/null @@ -1,337 +0,0 @@ -/* - * Universal Interface for Intel High Definition Audio Codec - * - * HD audio interface patch for Silicon Labs 3054/5 modem codec - * - * Copyright (c) 2005 Sasha Khapyorsky <sashak@alsa-project.org> - * Takashi Iwai <tiwai@suse.de> - * - * - * 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 <linux/delay.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <sound/core.h> -#include "hda_codec.h" -#include "hda_local.h" - -/* si3054 verbs */ -#define SI3054_VERB_READ_NODE 0x900 -#define SI3054_VERB_WRITE_NODE 0x100 - -/* si3054 nodes (registers) */ -#define SI3054_EXTENDED_MID 2 -#define SI3054_LINE_RATE 3 -#define SI3054_LINE_LEVEL 4 -#define SI3054_GPIO_CFG 5 -#define SI3054_GPIO_POLARITY 6 -#define SI3054_GPIO_STICKY 7 -#define SI3054_GPIO_WAKEUP 8 -#define SI3054_GPIO_STATUS 9 -#define SI3054_GPIO_CONTROL 10 -#define SI3054_MISC_AFE 11 -#define SI3054_CHIPID 12 -#define SI3054_LINE_CFG1 13 -#define SI3054_LINE_STATUS 14 -#define SI3054_DC_TERMINATION 15 -#define SI3054_LINE_CONFIG 16 -#define SI3054_CALLPROG_ATT 17 -#define SI3054_SQ_CONTROL 18 -#define SI3054_MISC_CONTROL 19 -#define SI3054_RING_CTRL1 20 -#define SI3054_RING_CTRL2 21 - -/* extended MID */ -#define SI3054_MEI_READY 0xf - -/* line level */ -#define SI3054_ATAG_MASK 0x00f0 -#define SI3054_DTAG_MASK 0xf000 - -/* GPIO bits */ -#define SI3054_GPIO_OH 0x0001 -#define SI3054_GPIO_CID 0x0002 - -/* chipid and revisions */ -#define SI3054_CHIPID_CODEC_REV_MASK 0x000f -#define SI3054_CHIPID_DAA_REV_MASK 0x00f0 -#define SI3054_CHIPID_INTERNATIONAL 0x0100 -#define SI3054_CHIPID_DAA_ID 0x0f00 -#define SI3054_CHIPID_CODEC_ID (1<<12) - -/* si3054 codec registers (nodes) access macros */ -#define GET_REG(codec,reg) (snd_hda_codec_read(codec,reg,0,SI3054_VERB_READ_NODE,0)) -#define SET_REG(codec,reg,val) (snd_hda_codec_write(codec,reg,0,SI3054_VERB_WRITE_NODE,val)) -#define SET_REG_CACHE(codec,reg,val) \ - snd_hda_codec_write_cache(codec,reg,0,SI3054_VERB_WRITE_NODE,val) - - -struct si3054_spec { - unsigned international; - struct hda_pcm pcm; -}; - - -/* - * Modem mixer - */ - -#define PRIVATE_VALUE(reg,mask) ((reg<<16)|(mask&0xffff)) -#define PRIVATE_REG(val) ((val>>16)&0xffff) -#define PRIVATE_MASK(val) (val&0xffff) - -#define si3054_switch_info snd_ctl_boolean_mono_info - -static int si3054_switch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *uvalue) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - u16 reg = PRIVATE_REG(kcontrol->private_value); - u16 mask = PRIVATE_MASK(kcontrol->private_value); - uvalue->value.integer.value[0] = (GET_REG(codec, reg)) & mask ? 1 : 0 ; - return 0; -} - -static int si3054_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *uvalue) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - u16 reg = PRIVATE_REG(kcontrol->private_value); - u16 mask = PRIVATE_MASK(kcontrol->private_value); - if (uvalue->value.integer.value[0]) - SET_REG_CACHE(codec, reg, (GET_REG(codec, reg)) | mask); - else - SET_REG_CACHE(codec, reg, (GET_REG(codec, reg)) & ~mask); - return 0; -} - -#define SI3054_KCONTROL(kname,reg,mask) { \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = kname, \ - .subdevice = HDA_SUBDEV_NID_FLAG | reg, \ - .info = si3054_switch_info, \ - .get = si3054_switch_get, \ - .put = si3054_switch_put, \ - .private_value = PRIVATE_VALUE(reg,mask), \ -} - - -static const struct snd_kcontrol_new si3054_modem_mixer[] = { - SI3054_KCONTROL("Off-hook Switch", SI3054_GPIO_CONTROL, SI3054_GPIO_OH), - SI3054_KCONTROL("Caller ID Switch", SI3054_GPIO_CONTROL, SI3054_GPIO_CID), - {} -}; - -static int si3054_build_controls(struct hda_codec *codec) -{ - return snd_hda_add_new_ctls(codec, si3054_modem_mixer); -} - - -/* - * PCM callbacks - */ - -static int si3054_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - u16 val; - - SET_REG(codec, SI3054_LINE_RATE, substream->runtime->rate); - val = GET_REG(codec, SI3054_LINE_LEVEL); - val &= 0xff << (8 * (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)); - val |= ((stream_tag & 0xf) << 4) << (8 * (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)); - SET_REG(codec, SI3054_LINE_LEVEL, val); - - snd_hda_codec_setup_stream(codec, hinfo->nid, - stream_tag, 0, format); - return 0; -} - -static int si3054_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - static unsigned int rates[] = { 8000, 9600, 16000 }; - static struct snd_pcm_hw_constraint_list hw_constraints_rates = { - .count = ARRAY_SIZE(rates), - .list = rates, - .mask = 0, - }; - substream->runtime->hw.period_bytes_min = 80; - return snd_pcm_hw_constraint_list(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); -} - - -static const struct hda_pcm_stream si3054_pcm = { - .substreams = 1, - .channels_min = 1, - .channels_max = 1, - .nid = 0x1, - .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_KNOT, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .maxbps = 16, - .ops = { - .open = si3054_pcm_open, - .prepare = si3054_pcm_prepare, - }, -}; - - -static int si3054_build_pcms(struct hda_codec *codec) -{ - struct si3054_spec *spec = codec->spec; - struct hda_pcm *info = &spec->pcm; - codec->num_pcms = 1; - codec->pcm_info = info; - info->name = "Si3054 Modem"; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = si3054_pcm; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = si3054_pcm; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = codec->mfg; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = codec->mfg; - info->pcm_type = HDA_PCM_TYPE_MODEM; - return 0; -} - - -/* - * Init part - */ - -static int si3054_init(struct hda_codec *codec) -{ - struct si3054_spec *spec = codec->spec; - unsigned wait_count; - u16 val; - - snd_hda_codec_write(codec, AC_NODE_ROOT, 0, AC_VERB_SET_CODEC_RESET, 0); - snd_hda_codec_write(codec, codec->mfg, 0, AC_VERB_SET_STREAM_FORMAT, 0); - SET_REG(codec, SI3054_LINE_RATE, 9600); - SET_REG(codec, SI3054_LINE_LEVEL, SI3054_DTAG_MASK|SI3054_ATAG_MASK); - SET_REG(codec, SI3054_EXTENDED_MID, 0); - - wait_count = 10; - do { - msleep(2); - val = GET_REG(codec, SI3054_EXTENDED_MID); - } while ((val & SI3054_MEI_READY) != SI3054_MEI_READY && wait_count--); - - if((val&SI3054_MEI_READY) != SI3054_MEI_READY) { - snd_printk(KERN_ERR "si3054: cannot initialize. EXT MID = %04x\n", val); - /* let's pray that this is no fatal error */ - /* return -EACCES; */ - } - - SET_REG(codec, SI3054_GPIO_POLARITY, 0xffff); - SET_REG(codec, SI3054_GPIO_CFG, 0x0); - SET_REG(codec, SI3054_MISC_AFE, 0); - SET_REG(codec, SI3054_LINE_CFG1,0x200); - - if((GET_REG(codec,SI3054_LINE_STATUS) & (1<<6)) == 0) { - snd_printd("Link Frame Detect(FDT) is not ready (line status: %04x)\n", - GET_REG(codec,SI3054_LINE_STATUS)); - } - - spec->international = GET_REG(codec, SI3054_CHIPID) & SI3054_CHIPID_INTERNATIONAL; - - return 0; -} - -static void si3054_free(struct hda_codec *codec) -{ - kfree(codec->spec); -} - - -/* - */ - -static const struct hda_codec_ops si3054_patch_ops = { - .build_controls = si3054_build_controls, - .build_pcms = si3054_build_pcms, - .init = si3054_init, - .free = si3054_free, -}; - -static int patch_si3054(struct hda_codec *codec) -{ - struct si3054_spec *spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - codec->spec = spec; - codec->patch_ops = si3054_patch_ops; - return 0; -} - -/* - * patch entries - */ -static const 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 }, - { .id = 0x11c13055, .name = "Si3054", .patch = patch_si3054 }, - { .id = 0x11c13155, .name = "Si3054", .patch = patch_si3054 }, - { .id = 0x10573055, .name = "Si3054", .patch = patch_si3054 }, - { .id = 0x10573057, .name = "Si3054", .patch = patch_si3054 }, - { .id = 0x10573155, .name = "Si3054", .patch = patch_si3054 }, - /* VIA HDA on Clevo m540 */ - { .id = 0x11063288, .name = "Si3054", .patch = patch_si3054 }, - /* Asus A8J Modem (SM56) */ - { .id = 0x15433155, .name = "Si3054", .patch = patch_si3054 }, - /* LG LW20 modem */ - { .id = 0x18540018, .name = "Si3054", .patch = patch_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/ANDROID_3.4.5/sound/pci/hda/patch_sigmatel.c b/ANDROID_3.4.5/sound/pci/hda/patch_sigmatel.c deleted file mode 100644 index 7494fbc1..00000000 --- a/ANDROID_3.4.5/sound/pci/hda/patch_sigmatel.c +++ /dev/null @@ -1,6515 +0,0 @@ -/* - * Universal Interface for Intel High Definition Audio Codec - * - * HD audio interface patch for SigmaTel STAC92xx - * - * Copyright (c) 2005 Embedded Alley Solutions, Inc. - * Matt Porter <mporter@embeddedalley.com> - * - * Based on patch_cmedia.c and patch_realtek.c - * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> - * - * 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 <linux/delay.h> -#include <linux/slab.h> -#include <linux/pci.h> -#include <linux/dmi.h> -#include <linux/module.h> -#include <sound/core.h> -#include <sound/asoundef.h> -#include <sound/jack.h> -#include <sound/tlv.h> -#include "hda_codec.h" -#include "hda_local.h" -#include "hda_beep.h" -#include "hda_jack.h" - -enum { - STAC_VREF_EVENT = 1, - STAC_INSERT_EVENT, - STAC_PWR_EVENT, - STAC_HP_EVENT, - STAC_LO_EVENT, - STAC_MIC_EVENT, -}; - -enum { - STAC_AUTO, - STAC_REF, - STAC_9200_OQO, - STAC_9200_DELL_D21, - STAC_9200_DELL_D22, - STAC_9200_DELL_D23, - STAC_9200_DELL_M21, - STAC_9200_DELL_M22, - STAC_9200_DELL_M23, - STAC_9200_DELL_M24, - STAC_9200_DELL_M25, - STAC_9200_DELL_M26, - STAC_9200_DELL_M27, - STAC_9200_M4, - STAC_9200_M4_2, - STAC_9200_PANASONIC, - STAC_9200_MODELS -}; - -enum { - STAC_9205_AUTO, - STAC_9205_REF, - STAC_9205_DELL_M42, - STAC_9205_DELL_M43, - STAC_9205_DELL_M44, - STAC_9205_EAPD, - STAC_9205_MODELS -}; - -enum { - STAC_92HD73XX_AUTO, - STAC_92HD73XX_NO_JD, /* no jack-detection */ - STAC_92HD73XX_REF, - STAC_92HD73XX_INTEL, - STAC_DELL_M6_AMIC, - STAC_DELL_M6_DMIC, - STAC_DELL_M6_BOTH, - STAC_DELL_EQ, - STAC_ALIENWARE_M17X, - STAC_92HD73XX_MODELS -}; - -enum { - STAC_92HD83XXX_AUTO, - STAC_92HD83XXX_REF, - STAC_92HD83XXX_PWR_REF, - STAC_DELL_S14, - STAC_DELL_VOSTRO_3500, - STAC_92HD83XXX_HP_cNB11_INTQUAD, - STAC_HP_DV7_4000, - STAC_HP_ZEPHYR, - STAC_92HD83XXX_MODELS -}; - -enum { - STAC_92HD71BXX_AUTO, - STAC_92HD71BXX_REF, - STAC_DELL_M4_1, - STAC_DELL_M4_2, - STAC_DELL_M4_3, - STAC_HP_M4, - STAC_HP_DV4, - STAC_HP_DV5, - STAC_HP_HDX, - STAC_HP_DV4_1222NR, - STAC_92HD71BXX_MODELS -}; - -enum { - STAC_925x_AUTO, - STAC_925x_REF, - STAC_M1, - STAC_M1_2, - STAC_M2, - STAC_M2_2, - STAC_M3, - STAC_M5, - STAC_M6, - STAC_925x_MODELS -}; - -enum { - STAC_922X_AUTO, - STAC_D945_REF, - STAC_D945GTP3, - STAC_D945GTP5, - STAC_INTEL_MAC_V1, - STAC_INTEL_MAC_V2, - STAC_INTEL_MAC_V3, - STAC_INTEL_MAC_V4, - STAC_INTEL_MAC_V5, - STAC_INTEL_MAC_AUTO, /* This model is selected if no module parameter - * is given, one of the above models will be - * chosen according to the subsystem id. */ - /* for backward compatibility */ - STAC_MACMINI, - STAC_MACBOOK, - STAC_MACBOOK_PRO_V1, - STAC_MACBOOK_PRO_V2, - STAC_IMAC_INTEL, - STAC_IMAC_INTEL_20, - STAC_ECS_202, - STAC_922X_DELL_D81, - STAC_922X_DELL_D82, - STAC_922X_DELL_M81, - STAC_922X_DELL_M82, - STAC_922X_MODELS -}; - -enum { - STAC_927X_AUTO, - STAC_D965_REF_NO_JD, /* no jack-detection */ - STAC_D965_REF, - STAC_D965_3ST, - STAC_D965_5ST, - STAC_D965_5ST_NO_FP, - STAC_DELL_3ST, - STAC_DELL_BIOS, - STAC_927X_VOLKNOB, - STAC_927X_MODELS -}; - -enum { - STAC_9872_AUTO, - STAC_9872_VAIO, - STAC_9872_MODELS -}; - -struct sigmatel_mic_route { - hda_nid_t pin; - signed char mux_idx; - signed char dmux_idx; -}; - -#define MAX_PINS_NUM 16 -#define MAX_ADCS_NUM 4 -#define MAX_DMICS_NUM 4 - -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 alt_switch: 1; - unsigned int hp_detect: 1; - unsigned int spdif_mute: 1; - unsigned int check_volume_offset:1; - unsigned int auto_mic:1; - unsigned int linear_tone_beep:1; - - /* gpio lines */ - unsigned int eapd_mask; - unsigned int gpio_mask; - unsigned int gpio_dir; - unsigned int gpio_data; - unsigned int gpio_mute; - unsigned int gpio_led; - unsigned int gpio_led_polarity; - unsigned int vref_mute_led_nid; /* pin NID for mute-LED vref control */ - unsigned int vref_led; - - /* stream */ - unsigned int stream_delay; - - /* analog loopback */ - const struct snd_kcontrol_new *aloopback_ctl; - unsigned char aloopback_mask; - unsigned char aloopback_shift; - - /* power management */ - unsigned int num_pwrs; - const hda_nid_t *pwr_nids; - const hda_nid_t *dac_list; - - /* playback */ - struct hda_input_mux *mono_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]; - - int volume_offset; - - /* capture */ - const hda_nid_t *adc_nids; - unsigned int num_adcs; - const hda_nid_t *mux_nids; - unsigned int num_muxes; - const hda_nid_t *dmic_nids; - unsigned int num_dmics; - const hda_nid_t *dmux_nids; - unsigned int num_dmuxes; - const hda_nid_t *smux_nids; - unsigned int num_smuxes; - unsigned int num_analog_muxes; - - const unsigned long *capvols; /* amp-volume attr: HDA_COMPOSE_AMP_VAL() */ - const unsigned long *capsws; /* amp-mute attr: HDA_COMPOSE_AMP_VAL() */ - unsigned int num_caps; /* number of capture volume/switch elements */ - - struct sigmatel_mic_route ext_mic; - struct sigmatel_mic_route int_mic; - struct sigmatel_mic_route dock_mic; - - const char * const *spdif_labels; - - hda_nid_t dig_in_nid; - hda_nid_t mono_nid; - hda_nid_t anabeep_nid; - hda_nid_t digbeep_nid; - - /* pin widgets */ - const hda_nid_t *pin_nids; - unsigned int num_pins; - - /* codec specific stuff */ - const struct hda_verb *init; - const struct snd_kcontrol_new *mixer; - - /* capture source */ - struct hda_input_mux *dinput_mux; - unsigned int cur_dmux[2]; - struct hda_input_mux *input_mux; - unsigned int cur_mux[3]; - struct hda_input_mux *sinput_mux; - unsigned int cur_smux[2]; - unsigned int cur_amux; - hda_nid_t *amp_nids; - unsigned int powerdown_adcs; - - /* i/o switches */ - unsigned int io_switch[2]; - unsigned int clfe_swap; - 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; - struct snd_array kctls; - struct hda_input_mux private_dimux; - struct hda_input_mux private_imux; - struct hda_input_mux private_smux; - struct hda_input_mux private_mono_mux; - - /* auto spec */ - unsigned auto_pin_cnt; - hda_nid_t auto_pin_nids[MAX_PINS_NUM]; - unsigned auto_adc_cnt; - hda_nid_t auto_adc_nids[MAX_ADCS_NUM]; - hda_nid_t auto_mux_nids[MAX_ADCS_NUM]; - hda_nid_t auto_dmux_nids[MAX_ADCS_NUM]; - unsigned long auto_capvols[MAX_ADCS_NUM]; - unsigned auto_dmic_cnt; - hda_nid_t auto_dmic_nids[MAX_DMICS_NUM]; - - struct hda_vmaster_mute_hook vmaster_mute; -}; - -static const hda_nid_t stac9200_adc_nids[1] = { - 0x03, -}; - -static const hda_nid_t stac9200_mux_nids[1] = { - 0x0c, -}; - -static const hda_nid_t stac9200_dac_nids[1] = { - 0x02, -}; - -static const hda_nid_t stac92hd73xx_pwr_nids[8] = { - 0x0a, 0x0b, 0x0c, 0xd, 0x0e, - 0x0f, 0x10, 0x11 -}; - -static const hda_nid_t stac92hd73xx_slave_dig_outs[2] = { - 0x26, 0, -}; - -static const hda_nid_t stac92hd73xx_adc_nids[2] = { - 0x1a, 0x1b -}; - -#define STAC92HD73XX_NUM_DMICS 2 -static const hda_nid_t stac92hd73xx_dmic_nids[STAC92HD73XX_NUM_DMICS + 1] = { - 0x13, 0x14, 0 -}; - -#define STAC92HD73_DAC_COUNT 5 - -static const hda_nid_t stac92hd73xx_mux_nids[2] = { - 0x20, 0x21, -}; - -static const hda_nid_t stac92hd73xx_dmux_nids[2] = { - 0x20, 0x21, -}; - -static const hda_nid_t stac92hd73xx_smux_nids[2] = { - 0x22, 0x23, -}; - -#define STAC92HD73XX_NUM_CAPS 2 -static const unsigned long stac92hd73xx_capvols[] = { - HDA_COMPOSE_AMP_VAL(0x20, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), -}; -#define stac92hd73xx_capsws stac92hd73xx_capvols - -#define STAC92HD83_DAC_COUNT 3 - -static const hda_nid_t stac92hd83xxx_pwr_nids[7] = { - 0x0a, 0x0b, 0x0c, 0xd, 0x0e, - 0x0f, 0x10 -}; - -static const hda_nid_t stac92hd83xxx_slave_dig_outs[2] = { - 0x1e, 0, -}; - -static const hda_nid_t stac92hd83xxx_dmic_nids[] = { - 0x11, 0x20, -}; - -static const hda_nid_t stac92hd71bxx_pwr_nids[3] = { - 0x0a, 0x0d, 0x0f -}; - -static const hda_nid_t stac92hd71bxx_adc_nids[2] = { - 0x12, 0x13, -}; - -static const hda_nid_t stac92hd71bxx_mux_nids[2] = { - 0x1a, 0x1b -}; - -static const hda_nid_t stac92hd71bxx_dmux_nids[2] = { - 0x1c, 0x1d, -}; - -static const hda_nid_t stac92hd71bxx_smux_nids[2] = { - 0x24, 0x25, -}; - -#define STAC92HD71BXX_NUM_DMICS 2 -static const hda_nid_t stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS + 1] = { - 0x18, 0x19, 0 -}; - -static const hda_nid_t stac92hd71bxx_dmic_5port_nids[STAC92HD71BXX_NUM_DMICS] = { - 0x18, 0 -}; - -static const hda_nid_t stac92hd71bxx_slave_dig_outs[2] = { - 0x22, 0 -}; - -#define STAC92HD71BXX_NUM_CAPS 2 -static const unsigned long stac92hd71bxx_capvols[] = { - HDA_COMPOSE_AMP_VAL(0x1c, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT), -}; -#define stac92hd71bxx_capsws stac92hd71bxx_capvols - -static const hda_nid_t stac925x_adc_nids[1] = { - 0x03, -}; - -static const hda_nid_t stac925x_mux_nids[1] = { - 0x0f, -}; - -static const hda_nid_t stac925x_dac_nids[1] = { - 0x02, -}; - -#define STAC925X_NUM_DMICS 1 -static const hda_nid_t stac925x_dmic_nids[STAC925X_NUM_DMICS + 1] = { - 0x15, 0 -}; - -static const hda_nid_t stac925x_dmux_nids[1] = { - 0x14, -}; - -static const unsigned long stac925x_capvols[] = { - HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_OUTPUT), -}; -static const unsigned long stac925x_capsws[] = { - HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), -}; - -static const hda_nid_t stac922x_adc_nids[2] = { - 0x06, 0x07, -}; - -static const hda_nid_t stac922x_mux_nids[2] = { - 0x12, 0x13, -}; - -#define STAC922X_NUM_CAPS 2 -static const unsigned long stac922x_capvols[] = { - HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_INPUT), - HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT), -}; -#define stac922x_capsws stac922x_capvols - -static const hda_nid_t stac927x_slave_dig_outs[2] = { - 0x1f, 0, -}; - -static const hda_nid_t stac927x_adc_nids[3] = { - 0x07, 0x08, 0x09 -}; - -static const hda_nid_t stac927x_mux_nids[3] = { - 0x15, 0x16, 0x17 -}; - -static const hda_nid_t stac927x_smux_nids[1] = { - 0x21, -}; - -static const hda_nid_t stac927x_dac_nids[6] = { - 0x02, 0x03, 0x04, 0x05, 0x06, 0 -}; - -static const hda_nid_t stac927x_dmux_nids[1] = { - 0x1b, -}; - -#define STAC927X_NUM_DMICS 2 -static const hda_nid_t stac927x_dmic_nids[STAC927X_NUM_DMICS + 1] = { - 0x13, 0x14, 0 -}; - -#define STAC927X_NUM_CAPS 3 -static const unsigned long stac927x_capvols[] = { - HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT), - HDA_COMPOSE_AMP_VAL(0x19, 3, 0, HDA_INPUT), - HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_INPUT), -}; -static const unsigned long stac927x_capsws[] = { - HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x1c, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT), -}; - -static const char * const stac927x_spdif_labels[5] = { - "Digital Playback", "ADAT", "Analog Mux 1", - "Analog Mux 2", "Analog Mux 3" -}; - -static const hda_nid_t stac9205_adc_nids[2] = { - 0x12, 0x13 -}; - -static const hda_nid_t stac9205_mux_nids[2] = { - 0x19, 0x1a -}; - -static const hda_nid_t stac9205_dmux_nids[1] = { - 0x1d, -}; - -static const hda_nid_t stac9205_smux_nids[1] = { - 0x21, -}; - -#define STAC9205_NUM_DMICS 2 -static const hda_nid_t stac9205_dmic_nids[STAC9205_NUM_DMICS + 1] = { - 0x17, 0x18, 0 -}; - -#define STAC9205_NUM_CAPS 2 -static const unsigned long stac9205_capvols[] = { - HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_INPUT), - HDA_COMPOSE_AMP_VAL(0x1c, 3, 0, HDA_INPUT), -}; -static const unsigned long stac9205_capsws[] = { - HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x1e, 3, 0, HDA_OUTPUT), -}; - -static const hda_nid_t stac9200_pin_nids[8] = { - 0x08, 0x09, 0x0d, 0x0e, - 0x0f, 0x10, 0x11, 0x12, -}; - -static const hda_nid_t stac925x_pin_nids[8] = { - 0x07, 0x08, 0x0a, 0x0b, - 0x0c, 0x0d, 0x10, 0x11, -}; - -static const hda_nid_t stac922x_pin_nids[10] = { - 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, - 0x0f, 0x10, 0x11, 0x15, 0x1b, -}; - -static const hda_nid_t stac92hd73xx_pin_nids[13] = { - 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, - 0x0f, 0x10, 0x11, 0x12, 0x13, - 0x14, 0x22, 0x23 -}; - -#define STAC92HD71BXX_NUM_PINS 13 -static const hda_nid_t stac92hd71bxx_pin_nids_4port[STAC92HD71BXX_NUM_PINS] = { - 0x0a, 0x0b, 0x0c, 0x0d, 0x00, - 0x00, 0x14, 0x18, 0x19, 0x1e, - 0x1f, 0x20, 0x27 -}; -static const hda_nid_t stac92hd71bxx_pin_nids_6port[STAC92HD71BXX_NUM_PINS] = { - 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, - 0x0f, 0x14, 0x18, 0x19, 0x1e, - 0x1f, 0x20, 0x27 -}; - -static const hda_nid_t stac927x_pin_nids[14] = { - 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, - 0x0f, 0x10, 0x11, 0x12, 0x13, - 0x14, 0x21, 0x22, 0x23, -}; - -static const hda_nid_t stac9205_pin_nids[12] = { - 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, - 0x0f, 0x14, 0x16, 0x17, 0x18, - 0x21, 0x22, -}; - -static int stac92xx_dmux_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - return snd_hda_input_mux_info(spec->dinput_mux, uinfo); -} - -static int stac92xx_dmux_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - unsigned int dmux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - - ucontrol->value.enumerated.item[0] = spec->cur_dmux[dmux_idx]; - return 0; -} - -static int stac92xx_dmux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - unsigned int dmux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - - return snd_hda_input_mux_put(codec, spec->dinput_mux, ucontrol, - spec->dmux_nids[dmux_idx], &spec->cur_dmux[dmux_idx]); -} - -static int stac92xx_smux_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - return snd_hda_input_mux_info(spec->sinput_mux, uinfo); -} - -static int stac92xx_smux_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - - ucontrol->value.enumerated.item[0] = spec->cur_smux[smux_idx]; - return 0; -} - -static int stac92xx_smux_enum_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; - struct hda_input_mux *smux = &spec->private_smux; - unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - int err, val; - hda_nid_t nid; - - err = snd_hda_input_mux_put(codec, spec->sinput_mux, ucontrol, - spec->smux_nids[smux_idx], &spec->cur_smux[smux_idx]); - if (err < 0) - return err; - - if (spec->spdif_mute) { - if (smux_idx == 0) - nid = spec->multiout.dig_out_nid; - else - nid = codec->slave_dig_outs[smux_idx - 1]; - if (spec->cur_smux[smux_idx] == smux->num_items - 1) - val = HDA_AMP_MUTE; - else - val = 0; - /* un/mute SPDIF out */ - snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, - HDA_AMP_MUTE, val); - } - return 0; -} - -static int stac_vrefout_set(struct hda_codec *codec, - hda_nid_t nid, unsigned int new_vref) -{ - int error, pinctl; - - snd_printdd("%s, nid %x ctl %x\n", __func__, nid, new_vref); - pinctl = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - - if (pinctl < 0) - return pinctl; - - pinctl &= 0xff; - pinctl &= ~AC_PINCTL_VREFEN; - pinctl |= (new_vref & AC_PINCTL_VREFEN); - - error = snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl); - if (error < 0) - return error; - - return 1; -} - -static unsigned int stac92xx_vref_set(struct hda_codec *codec, - hda_nid_t nid, unsigned int new_vref) -{ - int error; - unsigned int pincfg; - pincfg = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - - pincfg &= 0xff; - pincfg &= ~(AC_PINCTL_VREFEN | AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); - pincfg |= new_vref; - - if (new_vref == AC_PINCTL_VREF_HIZ) - pincfg |= AC_PINCTL_OUT_EN; - else - pincfg |= AC_PINCTL_IN_EN; - - error = snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, pincfg); - if (error < 0) - return error; - else - return 1; -} - -static unsigned int stac92xx_vref_get(struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int vref; - vref = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - vref &= AC_PINCTL_VREFEN; - return vref; -} - -static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - return snd_hda_input_mux_info(spec->input_mux, uinfo); -} - -static int stac92xx_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - - ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; - return 0; -} - -static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - const struct hda_input_mux *imux = spec->input_mux; - unsigned int idx, prev_idx, didx; - - idx = ucontrol->value.enumerated.item[0]; - if (idx >= imux->num_items) - idx = imux->num_items - 1; - prev_idx = spec->cur_mux[adc_idx]; - if (prev_idx == idx) - return 0; - if (idx < spec->num_analog_muxes) { - snd_hda_codec_write_cache(codec, spec->mux_nids[adc_idx], 0, - AC_VERB_SET_CONNECT_SEL, - imux->items[idx].index); - if (prev_idx >= spec->num_analog_muxes && - spec->mux_nids[adc_idx] != spec->dmux_nids[adc_idx]) { - imux = spec->dinput_mux; - /* 0 = analog */ - snd_hda_codec_write_cache(codec, - spec->dmux_nids[adc_idx], 0, - AC_VERB_SET_CONNECT_SEL, - imux->items[0].index); - } - } else { - imux = spec->dinput_mux; - /* first dimux item is hardcoded to select analog imux, - * so lets skip it - */ - didx = idx - spec->num_analog_muxes + 1; - snd_hda_codec_write_cache(codec, spec->dmux_nids[adc_idx], 0, - AC_VERB_SET_CONNECT_SEL, - imux->items[didx].index); - } - spec->cur_mux[adc_idx] = idx; - return 1; -} - -static int stac92xx_mono_mux_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - return snd_hda_input_mux_info(spec->mono_mux, uinfo); -} - -static int stac92xx_mono_mux_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - - ucontrol->value.enumerated.item[0] = spec->cur_mmux; - return 0; -} - -static int stac92xx_mono_mux_enum_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; - - return snd_hda_input_mux_put(codec, spec->mono_mux, ucontrol, - spec->mono_nid, &spec->cur_mmux); -} - -#define stac92xx_aloopback_info snd_ctl_boolean_mono_info - -static int stac92xx_aloopback_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - struct sigmatel_spec *spec = codec->spec; - - ucontrol->value.integer.value[0] = !!(spec->aloopback & - (spec->aloopback_mask << idx)); - return 0; -} - -static int stac92xx_aloopback_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - unsigned int dac_mode; - unsigned int val, idx_val; - - idx_val = spec->aloopback_mask << idx; - if (ucontrol->value.integer.value[0]) - val = spec->aloopback | idx_val; - else - val = spec->aloopback & ~idx_val; - if (spec->aloopback == val) - return 0; - - spec->aloopback = val; - - /* Only return the bits defined by the shift value of the - * first two bytes of the mask - */ - dac_mode = snd_hda_codec_read(codec, codec->afg, 0, - kcontrol->private_value & 0xFFFF, 0x0); - dac_mode >>= spec->aloopback_shift; - - if (spec->aloopback & idx_val) { - snd_hda_power_up(codec); - dac_mode |= idx_val; - } else { - snd_hda_power_down(codec); - dac_mode &= ~idx_val; - } - - snd_hda_codec_write_cache(codec, codec->afg, 0, - kcontrol->private_value >> 16, dac_mode); - - return 1; -} - -static const struct hda_verb stac9200_core_init[] = { - /* set dac0mux for dac converter */ - { 0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, - {} -}; - -static const struct hda_verb stac9200_eapd_init[] = { - /* set dac0mux for dac converter */ - {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x08, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, - {} -}; - -static const 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}, - {} -}; - -static const struct hda_verb stac92hd73xx_core_init[] = { - /* set master volume and direct control */ - { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, - {} -}; - -static const struct hda_verb stac92hd83xxx_core_init[] = { - /* power state controls amps */ - { 0x01, AC_VERB_SET_EAPD, 1 << 2}, - {} -}; - -static const struct hda_verb stac92hd83xxx_hp_zephyr_init[] = { - { 0x22, 0x785, 0x43 }, - { 0x22, 0x782, 0xe0 }, - { 0x22, 0x795, 0x00 }, - {} -}; - -static const struct hda_verb stac92hd71bxx_core_init[] = { - /* set master volume and direct control */ - { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, - {} -}; - -static const struct hda_verb stac92hd71bxx_unmute_core_init[] = { - /* unmute right and left channels for nodes 0x0f, 0xa, 0x0d */ - { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {} -}; - -static const struct hda_verb stac925x_core_init[] = { - /* set dac0mux for dac converter */ - { 0x06, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* mute the master volume */ - { 0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - {} -}; - -static const struct hda_verb stac922x_core_init[] = { - /* set master volume and direct control */ - { 0x16, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, - {} -}; - -static const struct hda_verb d965_core_init[] = { - /* set master volume and direct control */ - { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, - /* unmute node 0x1b */ - { 0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - /* select node 0x03 as DAC */ - { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x01}, - {} -}; - -static const struct hda_verb dell_3st_core_init[] = { - /* don't set delta bit */ - {0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0x7f}, - /* unmute node 0x1b */ - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - /* select node 0x03 as DAC */ - {0x0b, AC_VERB_SET_CONNECT_SEL, 0x01}, - {} -}; - -static const struct hda_verb stac927x_core_init[] = { - /* set master volume and direct control */ - { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, - /* enable analog pc beep path */ - { 0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5}, - {} -}; - -static const struct hda_verb stac927x_volknob_core_init[] = { - /* don't set delta bit */ - {0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0x7f}, - /* enable analog pc beep path */ - {0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5}, - {} -}; - -static const struct hda_verb stac9205_core_init[] = { - /* set master volume and direct control */ - { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, - /* enable analog pc beep path */ - { 0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5}, - {} -}; - -#define STAC_MONO_MUX \ - { \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = "Mono Mux", \ - .count = 1, \ - .info = stac92xx_mono_mux_enum_info, \ - .get = stac92xx_mono_mux_enum_get, \ - .put = stac92xx_mono_mux_enum_put, \ - } - -#define STAC_ANALOG_LOOPBACK(verb_read, verb_write, cnt) \ - { \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = "Analog Loopback", \ - .count = cnt, \ - .info = stac92xx_aloopback_info, \ - .get = stac92xx_aloopback_get, \ - .put = stac92xx_aloopback_put, \ - .private_value = verb_read | (verb_write << 16), \ - } - -#define DC_BIAS(xname, idx, nid) \ - { \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = idx, \ - .info = stac92xx_dc_bias_info, \ - .get = stac92xx_dc_bias_get, \ - .put = stac92xx_dc_bias_put, \ - .private_value = nid, \ - } - -static const struct snd_kcontrol_new stac9200_mixer[] = { - HDA_CODEC_VOLUME_MIN_MUTE("PCM Playback Volume", 0xb, 0, HDA_OUTPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0xb, 0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new stac92hd73xx_6ch_loopback[] = { - STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3), - {} -}; - -static const struct snd_kcontrol_new stac92hd73xx_8ch_loopback[] = { - STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 4), - {} -}; - -static const struct snd_kcontrol_new stac92hd73xx_10ch_loopback[] = { - STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 5), - {} -}; - - -static const struct snd_kcontrol_new stac92hd71bxx_loopback[] = { - STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2) -}; - -static const struct snd_kcontrol_new stac925x_mixer[] = { - HDA_CODEC_VOLUME_MIN_MUTE("PCM Playback Volume", 0xe, 0, HDA_OUTPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x0e, 0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new stac9205_loopback[] = { - STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0, 1), - {} -}; - -static const struct snd_kcontrol_new stac927x_loopback[] = { - STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1), - {} -}; - -static struct snd_kcontrol_new stac_dmux_mixer = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Digital Input Source", - /* count set later */ - .info = stac92xx_dmux_enum_info, - .get = stac92xx_dmux_enum_get, - .put = stac92xx_dmux_enum_put, -}; - -static struct snd_kcontrol_new stac_smux_mixer = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "IEC958 Playback Source", - /* count set later */ - .info = stac92xx_smux_enum_info, - .get = stac92xx_smux_enum_get, - .put = stac92xx_smux_enum_put, -}; - -static const char * const slave_pfxs[] = { - "Front", "Surround", "Center", "LFE", "Side", - "Headphone", "Speaker", "IEC958", - NULL -}; - -static void stac92xx_update_led_status(struct hda_codec *codec, int enabled); - -static void stac92xx_vmaster_hook(void *private_data, int val) -{ - stac92xx_update_led_status(private_data, val); -} - -static void stac92xx_free_kctls(struct hda_codec *codec); - -static int stac92xx_build_controls(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - unsigned int vmaster_tlv[4]; - int err; - int i; - - if (spec->mixer) { - err = snd_hda_add_new_ctls(codec, spec->mixer); - if (err < 0) - return err; - } - - for (i = 0; i < spec->num_mixers; i++) { - err = snd_hda_add_new_ctls(codec, spec->mixers[i]); - if (err < 0) - return err; - } - if (!spec->auto_mic && spec->num_dmuxes > 0 && - snd_hda_get_bool_hint(codec, "separate_dmux") == 1) { - stac_dmux_mixer.count = spec->num_dmuxes; - err = snd_hda_ctl_add(codec, 0, - snd_ctl_new1(&stac_dmux_mixer, codec)); - if (err < 0) - return err; - } - if (spec->num_smuxes > 0) { - int wcaps = get_wcaps(codec, spec->multiout.dig_out_nid); - struct hda_input_mux *smux = &spec->private_smux; - /* check for mute support on SPDIF out */ - if (wcaps & AC_WCAP_OUT_AMP) { - snd_hda_add_imux_item(smux, "Off", 0, NULL); - spec->spdif_mute = 1; - } - stac_smux_mixer.count = spec->num_smuxes; - err = snd_hda_ctl_add(codec, 0, - snd_ctl_new1(&stac_smux_mixer, codec)); - if (err < 0) - return err; - } - - if (spec->multiout.dig_out_nid) { - err = snd_hda_create_spdif_out_ctls(codec, - spec->multiout.dig_out_nid, - spec->multiout.dig_out_nid); - if (err < 0) - return err; - err = snd_hda_create_spdif_share_sw(codec, - &spec->multiout); - if (err < 0) - return err; - spec->multiout.share_spdif = 1; - } - if (spec->dig_in_nid && !(spec->gpio_dir & 0x01)) { - err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); - if (err < 0) - return err; - } - - /* if we have no master control, let's create it */ - snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0], - HDA_OUTPUT, vmaster_tlv); - /* correct volume offset */ - vmaster_tlv[2] += vmaster_tlv[3] * spec->volume_offset; - /* minimum value is actually mute */ - vmaster_tlv[3] |= TLV_DB_SCALE_MUTE; - err = snd_hda_add_vmaster(codec, "Master Playback Volume", - vmaster_tlv, slave_pfxs, - "Playback Volume"); - if (err < 0) - return err; - - err = __snd_hda_add_vmaster(codec, "Master Playback Switch", - NULL, slave_pfxs, - "Playback Switch", true, - &spec->vmaster_mute.sw_kctl); - if (err < 0) - return err; - - if (spec->gpio_led) { - spec->vmaster_mute.hook = stac92xx_vmaster_hook; - err = snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true); - if (err < 0) - return err; - } - - if (spec->aloopback_ctl && - snd_hda_get_bool_hint(codec, "loopback") == 1) { - err = snd_hda_add_new_ctls(codec, spec->aloopback_ctl); - if (err < 0) - return err; - } - - stac92xx_free_kctls(codec); /* no longer needed */ - - err = snd_hda_jack_add_kctls(codec, &spec->autocfg); - if (err < 0) - return err; - - return 0; -} - -static const unsigned int ref9200_pin_configs[8] = { - 0x01c47010, 0x01447010, 0x0221401f, 0x01114010, - 0x02a19020, 0x01a19021, 0x90100140, 0x01813122, -}; - -static const unsigned int gateway9200_m4_pin_configs[8] = { - 0x400000fe, 0x404500f4, 0x400100f0, 0x90110010, - 0x400100f1, 0x02a1902e, 0x500000f2, 0x500000f3, -}; -static const unsigned int gateway9200_m4_2_pin_configs[8] = { - 0x400000fe, 0x404500f4, 0x400100f0, 0x90110010, - 0x400100f1, 0x02a1902e, 0x500000f2, 0x500000f3, -}; - -/* - STAC 9200 pin configs for - 102801A8 - 102801DE - 102801E8 -*/ -static const unsigned int dell9200_d21_pin_configs[8] = { - 0x400001f0, 0x400001f1, 0x02214030, 0x01014010, - 0x02a19020, 0x01a19021, 0x90100140, 0x01813122, -}; - -/* - STAC 9200 pin configs for - 102801C0 - 102801C1 -*/ -static const unsigned int dell9200_d22_pin_configs[8] = { - 0x400001f0, 0x400001f1, 0x0221401f, 0x01014010, - 0x01813020, 0x02a19021, 0x90100140, 0x400001f2, -}; - -/* - STAC 9200 pin configs for - 102801C4 (Dell Dimension E310) - 102801C5 - 102801C7 - 102801D9 - 102801DA - 102801E3 -*/ -static const unsigned int dell9200_d23_pin_configs[8] = { - 0x400001f0, 0x400001f1, 0x0221401f, 0x01014010, - 0x01813020, 0x01a19021, 0x90100140, 0x400001f2, -}; - - -/* - STAC 9200-32 pin configs for - 102801B5 (Dell Inspiron 630m) - 102801D8 (Dell Inspiron 640m) -*/ -static const unsigned int dell9200_m21_pin_configs[8] = { - 0x40c003fa, 0x03441340, 0x0321121f, 0x90170310, - 0x408003fb, 0x03a11020, 0x401003fc, 0x403003fd, -}; - -/* - STAC 9200-32 pin configs for - 102801C2 (Dell Latitude D620) - 102801C8 - 102801CC (Dell Latitude D820) - 102801D4 - 102801D6 -*/ -static const unsigned int dell9200_m22_pin_configs[8] = { - 0x40c003fa, 0x0144131f, 0x0321121f, 0x90170310, - 0x90a70321, 0x03a11020, 0x401003fb, 0x40f000fc, -}; - -/* - STAC 9200-32 pin configs for - 102801CE (Dell XPS M1710) - 102801CF (Dell Precision M90) -*/ -static const unsigned int dell9200_m23_pin_configs[8] = { - 0x40c003fa, 0x01441340, 0x0421421f, 0x90170310, - 0x408003fb, 0x04a1102e, 0x90170311, 0x403003fc, -}; - -/* - STAC 9200-32 pin configs for - 102801C9 - 102801CA - 102801CB (Dell Latitude 120L) - 102801D3 -*/ -static const unsigned int dell9200_m24_pin_configs[8] = { - 0x40c003fa, 0x404003fb, 0x0321121f, 0x90170310, - 0x408003fc, 0x03a11020, 0x401003fd, 0x403003fe, -}; - -/* - STAC 9200-32 pin configs for - 102801BD (Dell Inspiron E1505n) - 102801EE - 102801EF -*/ -static const unsigned int dell9200_m25_pin_configs[8] = { - 0x40c003fa, 0x01441340, 0x0421121f, 0x90170310, - 0x408003fb, 0x04a11020, 0x401003fc, 0x403003fd, -}; - -/* - STAC 9200-32 pin configs for - 102801F5 (Dell Inspiron 1501) - 102801F6 -*/ -static const unsigned int dell9200_m26_pin_configs[8] = { - 0x40c003fa, 0x404003fb, 0x0421121f, 0x90170310, - 0x408003fc, 0x04a11020, 0x401003fd, 0x403003fe, -}; - -/* - STAC 9200-32 - 102801CD (Dell Inspiron E1705/9400) -*/ -static const unsigned int dell9200_m27_pin_configs[8] = { - 0x40c003fa, 0x01441340, 0x0421121f, 0x90170310, - 0x90170310, 0x04a11020, 0x90170310, 0x40f003fc, -}; - -static const unsigned int oqo9200_pin_configs[8] = { - 0x40c000f0, 0x404000f1, 0x0221121f, 0x02211210, - 0x90170111, 0x90a70120, 0x400000f2, 0x400000f3, -}; - - -static const unsigned int *stac9200_brd_tbl[STAC_9200_MODELS] = { - [STAC_REF] = ref9200_pin_configs, - [STAC_9200_OQO] = oqo9200_pin_configs, - [STAC_9200_DELL_D21] = dell9200_d21_pin_configs, - [STAC_9200_DELL_D22] = dell9200_d22_pin_configs, - [STAC_9200_DELL_D23] = dell9200_d23_pin_configs, - [STAC_9200_DELL_M21] = dell9200_m21_pin_configs, - [STAC_9200_DELL_M22] = dell9200_m22_pin_configs, - [STAC_9200_DELL_M23] = dell9200_m23_pin_configs, - [STAC_9200_DELL_M24] = dell9200_m24_pin_configs, - [STAC_9200_DELL_M25] = dell9200_m25_pin_configs, - [STAC_9200_DELL_M26] = dell9200_m26_pin_configs, - [STAC_9200_DELL_M27] = dell9200_m27_pin_configs, - [STAC_9200_M4] = gateway9200_m4_pin_configs, - [STAC_9200_M4_2] = gateway9200_m4_2_pin_configs, - [STAC_9200_PANASONIC] = ref9200_pin_configs, -}; - -static const char * const stac9200_models[STAC_9200_MODELS] = { - [STAC_AUTO] = "auto", - [STAC_REF] = "ref", - [STAC_9200_OQO] = "oqo", - [STAC_9200_DELL_D21] = "dell-d21", - [STAC_9200_DELL_D22] = "dell-d22", - [STAC_9200_DELL_D23] = "dell-d23", - [STAC_9200_DELL_M21] = "dell-m21", - [STAC_9200_DELL_M22] = "dell-m22", - [STAC_9200_DELL_M23] = "dell-m23", - [STAC_9200_DELL_M24] = "dell-m24", - [STAC_9200_DELL_M25] = "dell-m25", - [STAC_9200_DELL_M26] = "dell-m26", - [STAC_9200_DELL_M27] = "dell-m27", - [STAC_9200_M4] = "gateway-m4", - [STAC_9200_M4_2] = "gateway-m4-2", - [STAC_9200_PANASONIC] = "panasonic", -}; - -static const struct snd_pci_quirk stac9200_cfg_tbl[] = { - /* SigmaTel reference board */ - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, - "DFI LanParty", STAC_REF), - SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, - "DFI LanParty", STAC_REF), - /* Dell laptops have BIOS problem */ - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a8, - "unknown Dell", STAC_9200_DELL_D21), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01b5, - "Dell Inspiron 630m", STAC_9200_DELL_M21), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01bd, - "Dell Inspiron E1505n", STAC_9200_DELL_M25), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c0, - "unknown Dell", STAC_9200_DELL_D22), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c1, - "unknown Dell", STAC_9200_DELL_D22), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c2, - "Dell Latitude D620", STAC_9200_DELL_M22), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c5, - "unknown Dell", STAC_9200_DELL_D23), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c7, - "unknown Dell", STAC_9200_DELL_D23), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c8, - "unknown Dell", STAC_9200_DELL_M22), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c9, - "unknown Dell", STAC_9200_DELL_M24), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ca, - "unknown Dell", STAC_9200_DELL_M24), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cb, - "Dell Latitude 120L", STAC_9200_DELL_M24), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cc, - "Dell Latitude D820", STAC_9200_DELL_M22), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cd, - "Dell Inspiron E1705/9400", STAC_9200_DELL_M27), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ce, - "Dell XPS M1710", STAC_9200_DELL_M23), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cf, - "Dell Precision M90", STAC_9200_DELL_M23), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d3, - "unknown Dell", STAC_9200_DELL_M22), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d4, - "unknown Dell", STAC_9200_DELL_M22), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d6, - "unknown Dell", STAC_9200_DELL_M22), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d8, - "Dell Inspiron 640m", STAC_9200_DELL_M21), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d9, - "unknown Dell", STAC_9200_DELL_D23), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01da, - "unknown Dell", STAC_9200_DELL_D23), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01de, - "unknown Dell", STAC_9200_DELL_D21), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01e3, - "unknown Dell", STAC_9200_DELL_D23), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01e8, - "unknown Dell", STAC_9200_DELL_D21), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ee, - "unknown Dell", STAC_9200_DELL_M25), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ef, - "unknown Dell", STAC_9200_DELL_M25), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f5, - "Dell Inspiron 1501", STAC_9200_DELL_M26), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f6, - "unknown Dell", STAC_9200_DELL_M26), - /* Panasonic */ - SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-74", STAC_9200_PANASONIC), - /* Gateway machines needs EAPD to be set on resume */ - SND_PCI_QUIRK(0x107b, 0x0205, "Gateway S-7110M", STAC_9200_M4), - SND_PCI_QUIRK(0x107b, 0x0317, "Gateway MT3423, MX341*", STAC_9200_M4_2), - SND_PCI_QUIRK(0x107b, 0x0318, "Gateway ML3019, MT3707", STAC_9200_M4_2), - /* OQO Mobile */ - SND_PCI_QUIRK(0x1106, 0x3288, "OQO Model 2", STAC_9200_OQO), - {} /* terminator */ -}; - -static const unsigned int ref925x_pin_configs[8] = { - 0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021, - 0x90a70320, 0x02214210, 0x01019020, 0x9033032e, -}; - -static const unsigned int stac925xM1_pin_configs[8] = { - 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020, - 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e, -}; - -static const unsigned int stac925xM1_2_pin_configs[8] = { - 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020, - 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e, -}; - -static const unsigned int stac925xM2_pin_configs[8] = { - 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020, - 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e, -}; - -static const unsigned int stac925xM2_2_pin_configs[8] = { - 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020, - 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e, -}; - -static const unsigned int stac925xM3_pin_configs[8] = { - 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020, - 0x40a000f0, 0x90100210, 0x400003f1, 0x503303f3, -}; - -static const unsigned int stac925xM5_pin_configs[8] = { - 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020, - 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e, -}; - -static const unsigned int stac925xM6_pin_configs[8] = { - 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020, - 0x40a000f0, 0x90100210, 0x400003f1, 0x90330320, -}; - -static const unsigned int *stac925x_brd_tbl[STAC_925x_MODELS] = { - [STAC_REF] = ref925x_pin_configs, - [STAC_M1] = stac925xM1_pin_configs, - [STAC_M1_2] = stac925xM1_2_pin_configs, - [STAC_M2] = stac925xM2_pin_configs, - [STAC_M2_2] = stac925xM2_2_pin_configs, - [STAC_M3] = stac925xM3_pin_configs, - [STAC_M5] = stac925xM5_pin_configs, - [STAC_M6] = stac925xM6_pin_configs, -}; - -static const char * const stac925x_models[STAC_925x_MODELS] = { - [STAC_925x_AUTO] = "auto", - [STAC_REF] = "ref", - [STAC_M1] = "m1", - [STAC_M1_2] = "m1-2", - [STAC_M2] = "m2", - [STAC_M2_2] = "m2-2", - [STAC_M3] = "m3", - [STAC_M5] = "m5", - [STAC_M6] = "m6", -}; - -static const struct snd_pci_quirk stac925x_codec_id_cfg_tbl[] = { - SND_PCI_QUIRK(0x107b, 0x0316, "Gateway M255", STAC_M2), - SND_PCI_QUIRK(0x107b, 0x0366, "Gateway MP6954", STAC_M5), - SND_PCI_QUIRK(0x107b, 0x0461, "Gateway NX560XL", STAC_M1), - SND_PCI_QUIRK(0x107b, 0x0681, "Gateway NX860", STAC_M2), - SND_PCI_QUIRK(0x107b, 0x0367, "Gateway MX6453", STAC_M1_2), - /* Not sure about the brand name for those */ - SND_PCI_QUIRK(0x107b, 0x0281, "Gateway mobile", STAC_M1), - SND_PCI_QUIRK(0x107b, 0x0507, "Gateway mobile", STAC_M3), - SND_PCI_QUIRK(0x107b, 0x0281, "Gateway mobile", STAC_M6), - SND_PCI_QUIRK(0x107b, 0x0685, "Gateway mobile", STAC_M2_2), - {} /* terminator */ -}; - -static const struct snd_pci_quirk stac925x_cfg_tbl[] = { - /* SigmaTel reference board */ - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_REF), - SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, "DFI LanParty", STAC_REF), - SND_PCI_QUIRK(0x8384, 0x7632, "Stac9202 Reference Board", STAC_REF), - - /* Default table for unknown ID */ - SND_PCI_QUIRK(0x1002, 0x437b, "Gateway mobile", STAC_M2_2), - - {} /* terminator */ -}; - -static const unsigned int ref92hd73xx_pin_configs[13] = { - 0x02214030, 0x02a19040, 0x01a19020, 0x02214030, - 0x0181302e, 0x01014010, 0x01014020, 0x01014030, - 0x02319040, 0x90a000f0, 0x90a000f0, 0x01452050, - 0x01452050, -}; - -static const unsigned int dell_m6_pin_configs[13] = { - 0x0321101f, 0x4f00000f, 0x4f0000f0, 0x90170110, - 0x03a11020, 0x0321101f, 0x4f0000f0, 0x4f0000f0, - 0x4f0000f0, 0x90a60160, 0x4f0000f0, 0x4f0000f0, - 0x4f0000f0, -}; - -static const unsigned int alienware_m17x_pin_configs[13] = { - 0x0321101f, 0x0321101f, 0x03a11020, 0x03014020, - 0x90170110, 0x4f0000f0, 0x4f0000f0, 0x4f0000f0, - 0x4f0000f0, 0x90a60160, 0x4f0000f0, 0x4f0000f0, - 0x904601b0, -}; - -static const unsigned int intel_dg45id_pin_configs[13] = { - 0x02214230, 0x02A19240, 0x01013214, 0x01014210, - 0x01A19250, 0x01011212, 0x01016211 -}; - -static const unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = { - [STAC_92HD73XX_REF] = ref92hd73xx_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, - [STAC_ALIENWARE_M17X] = alienware_m17x_pin_configs, - [STAC_92HD73XX_INTEL] = intel_dg45id_pin_configs, -}; - -static const char * const stac92hd73xx_models[STAC_92HD73XX_MODELS] = { - [STAC_92HD73XX_AUTO] = "auto", - [STAC_92HD73XX_NO_JD] = "no-jd", - [STAC_92HD73XX_REF] = "ref", - [STAC_92HD73XX_INTEL] = "intel", - [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", - [STAC_ALIENWARE_M17X] = "alienware", -}; - -static const struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = { - /* SigmaTel reference board */ - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, - "DFI LanParty", STAC_92HD73XX_REF), - SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, - "DFI LanParty", STAC_92HD73XX_REF), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5002, - "Intel DG45ID", STAC_92HD73XX_INTEL), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5003, - "Intel DG45FC", STAC_92HD73XX_INTEL), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0254, - "Dell Studio 1535", STAC_DELL_M6_DMIC), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0255, - "unknown Dell", STAC_DELL_M6_DMIC), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0256, - "unknown Dell", STAC_DELL_M6_BOTH), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0257, - "unknown Dell", STAC_DELL_M6_BOTH), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x025e, - "unknown Dell", STAC_DELL_M6_AMIC), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x025f, - "unknown Dell", STAC_DELL_M6_AMIC), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0271, - "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), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02be, - "Dell Studio 1555", STAC_DELL_M6_DMIC), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02bd, - "Dell Studio 1557", STAC_DELL_M6_DMIC), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02fe, - "Dell Studio XPS 1645", STAC_DELL_M6_DMIC), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0413, - "Dell Studio 1558", STAC_DELL_M6_DMIC), - {} /* terminator */ -}; - -static const struct snd_pci_quirk stac92hd73xx_codec_id_cfg_tbl[] = { - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02a1, - "Alienware M17x", STAC_ALIENWARE_M17X), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x043a, - "Alienware M17x", STAC_ALIENWARE_M17X), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0490, - "Alienware M17x R3", STAC_DELL_EQ), - {} /* terminator */ -}; - -static const unsigned int ref92hd83xxx_pin_configs[10] = { - 0x02214030, 0x02211010, 0x02a19020, 0x02170130, - 0x01014050, 0x01819040, 0x01014020, 0x90a3014e, - 0x01451160, 0x98560170, -}; - -static const unsigned int dell_s14_pin_configs[10] = { - 0x0221403f, 0x0221101f, 0x02a19020, 0x90170110, - 0x40f000f0, 0x40f000f0, 0x40f000f0, 0x90a60160, - 0x40f000f0, 0x40f000f0, -}; - -static const unsigned int dell_vostro_3500_pin_configs[10] = { - 0x02a11020, 0x0221101f, 0x400000f0, 0x90170110, - 0x400000f1, 0x400000f2, 0x400000f3, 0x90a60160, - 0x400000f4, 0x400000f5, -}; - -static const unsigned int hp_dv7_4000_pin_configs[10] = { - 0x03a12050, 0x0321201f, 0x40f000f0, 0x90170110, - 0x40f000f0, 0x40f000f0, 0x90170110, 0xd5a30140, - 0x40f000f0, 0x40f000f0, -}; - -static const unsigned int hp_zephyr_pin_configs[10] = { - 0x01813050, 0x0421201f, 0x04a1205e, 0x96130310, - 0x96130310, 0x0101401f, 0x1111611f, 0xd5a30130, - 0, 0, -}; - -static const unsigned int hp_cNB11_intquad_pin_configs[10] = { - 0x40f000f0, 0x0221101f, 0x02a11020, 0x92170110, - 0x40f000f0, 0x92170110, 0x40f000f0, 0xd5a30130, - 0x40f000f0, 0x40f000f0, -}; - -static const unsigned int *stac92hd83xxx_brd_tbl[STAC_92HD83XXX_MODELS] = { - [STAC_92HD83XXX_REF] = ref92hd83xxx_pin_configs, - [STAC_92HD83XXX_PWR_REF] = ref92hd83xxx_pin_configs, - [STAC_DELL_S14] = dell_s14_pin_configs, - [STAC_DELL_VOSTRO_3500] = dell_vostro_3500_pin_configs, - [STAC_92HD83XXX_HP_cNB11_INTQUAD] = hp_cNB11_intquad_pin_configs, - [STAC_HP_DV7_4000] = hp_dv7_4000_pin_configs, - [STAC_HP_ZEPHYR] = hp_zephyr_pin_configs, -}; - -static const char * const stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = { - [STAC_92HD83XXX_AUTO] = "auto", - [STAC_92HD83XXX_REF] = "ref", - [STAC_92HD83XXX_PWR_REF] = "mic-ref", - [STAC_DELL_S14] = "dell-s14", - [STAC_DELL_VOSTRO_3500] = "dell-vostro-3500", - [STAC_92HD83XXX_HP_cNB11_INTQUAD] = "hp_cNB11_intquad", - [STAC_HP_DV7_4000] = "hp-dv7-4000", - [STAC_HP_ZEPHYR] = "hp-zephyr", -}; - -static const struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = { - /* SigmaTel reference board */ - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, - "DFI LanParty", STAC_92HD83XXX_REF), - SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, - "DFI LanParty", STAC_92HD83XXX_REF), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ba, - "unknown Dell", STAC_DELL_S14), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x1028, - "Dell Vostro 3500", STAC_DELL_VOSTRO_3500), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1656, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1657, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1658, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1659, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x165A, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x165B, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3388, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3389, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355B, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355C, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355D, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355E, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355F, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3560, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x358B, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x358C, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x358D, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3591, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3592, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3593, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3561, - "HP", STAC_HP_ZEPHYR), - {} /* terminator */ -}; - -static const struct snd_pci_quirk stac92hd83xxx_codec_id_cfg_tbl[] = { - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3561, - "HP", STAC_HP_ZEPHYR), - {} /* terminator */ -}; - -static const unsigned int ref92hd71bxx_pin_configs[STAC92HD71BXX_NUM_PINS] = { - 0x02214030, 0x02a19040, 0x01a19020, 0x01014010, - 0x0181302e, 0x01014010, 0x01019020, 0x90a000f0, - 0x90a000f0, 0x01452050, 0x01452050, 0x00000000, - 0x00000000 -}; - -static const unsigned int dell_m4_1_pin_configs[STAC92HD71BXX_NUM_PINS] = { - 0x0421101f, 0x04a11221, 0x40f000f0, 0x90170110, - 0x23a1902e, 0x23014250, 0x40f000f0, 0x90a000f0, - 0x40f000f0, 0x4f0000f0, 0x4f0000f0, 0x00000000, - 0x00000000 -}; - -static const unsigned int dell_m4_2_pin_configs[STAC92HD71BXX_NUM_PINS] = { - 0x0421101f, 0x04a11221, 0x90a70330, 0x90170110, - 0x23a1902e, 0x23014250, 0x40f000f0, 0x40f000f0, - 0x40f000f0, 0x044413b0, 0x044413b0, 0x00000000, - 0x00000000 -}; - -static const unsigned int dell_m4_3_pin_configs[STAC92HD71BXX_NUM_PINS] = { - 0x0421101f, 0x04a11221, 0x90a70330, 0x90170110, - 0x40f000f0, 0x40f000f0, 0x40f000f0, 0x90a000f0, - 0x40f000f0, 0x044413b0, 0x044413b0, 0x00000000, - 0x00000000 -}; - -static const 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, - [STAC_HP_DV4] = NULL, - [STAC_HP_DV5] = NULL, - [STAC_HP_HDX] = NULL, - [STAC_HP_DV4_1222NR] = NULL, -}; - -static const char * const stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = { - [STAC_92HD71BXX_AUTO] = "auto", - [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", - [STAC_HP_DV4] = "hp-dv4", - [STAC_HP_DV5] = "hp-dv5", - [STAC_HP_HDX] = "hp-hdx", - [STAC_HP_DV4_1222NR] = "hp-dv4-1222nr", -}; - -static const 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_DFI, 0x3101, - "DFI LanParty", STAC_92HD71BXX_REF), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30fb, - "HP dv4-1222nr", STAC_HP_DV4_1222NR), - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x1720, - "HP", STAC_HP_DV5), - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3080, - "HP", STAC_HP_DV5), - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x30f0, - "HP dv4-7", STAC_HP_DV4), - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3600, - "HP dv4-7", STAC_HP_DV5), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3610, - "HP HDX", STAC_HP_HDX), /* HDX18 */ - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361a, - "HP mini 1000", STAC_HP_M4), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361b, - "HP HDX", STAC_HP_HDX), /* HDX16 */ - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3620, - "HP dv6", STAC_HP_DV5), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3061, - "HP dv6", STAC_HP_DV5), /* HP dv6-1110ax */ - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x363e, - "HP DV6", STAC_HP_DV5), - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x7010, - "HP", STAC_HP_DV5), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233, - "unknown Dell", STAC_DELL_M4_1), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0234, - "unknown Dell", STAC_DELL_M4_1), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0250, - "unknown Dell", STAC_DELL_M4_1), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x024f, - "unknown Dell", STAC_DELL_M4_1), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x024d, - "unknown Dell", STAC_DELL_M4_1), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0251, - "unknown Dell", STAC_DELL_M4_1), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0277, - "unknown Dell", STAC_DELL_M4_1), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0263, - "unknown Dell", STAC_DELL_M4_2), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0265, - "unknown Dell", STAC_DELL_M4_2), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0262, - "unknown Dell", STAC_DELL_M4_2), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0264, - "unknown Dell", STAC_DELL_M4_2), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02aa, - "unknown Dell", STAC_DELL_M4_3), - {} /* terminator */ -}; - -static const unsigned int ref922x_pin_configs[10] = { - 0x01014010, 0x01016011, 0x01012012, 0x0221401f, - 0x01813122, 0x01011014, 0x01441030, 0x01c41030, - 0x40000100, 0x40000100, -}; - -/* - STAC 922X pin configs for - 102801A7 - 102801AB - 102801A9 - 102801D1 - 102801D2 -*/ -static const unsigned int dell_922x_d81_pin_configs[10] = { - 0x02214030, 0x01a19021, 0x01111012, 0x01114010, - 0x02a19020, 0x01117011, 0x400001f0, 0x400001f1, - 0x01813122, 0x400001f2, -}; - -/* - STAC 922X pin configs for - 102801AC - 102801D0 -*/ -static const unsigned int dell_922x_d82_pin_configs[10] = { - 0x02214030, 0x01a19021, 0x01111012, 0x01114010, - 0x02a19020, 0x01117011, 0x01451140, 0x400001f0, - 0x01813122, 0x400001f1, -}; - -/* - STAC 922X pin configs for - 102801BF -*/ -static const unsigned int dell_922x_m81_pin_configs[10] = { - 0x0321101f, 0x01112024, 0x01111222, 0x91174220, - 0x03a11050, 0x01116221, 0x90a70330, 0x01452340, - 0x40C003f1, 0x405003f0, -}; - -/* - STAC 9221 A1 pin configs for - 102801D7 (Dell XPS M1210) -*/ -static const unsigned int dell_922x_m82_pin_configs[10] = { - 0x02211211, 0x408103ff, 0x02a1123e, 0x90100310, - 0x408003f1, 0x0221121f, 0x03451340, 0x40c003f2, - 0x508003f3, 0x405003f4, -}; - -static const unsigned int d945gtp3_pin_configs[10] = { - 0x0221401f, 0x01a19022, 0x01813021, 0x01014010, - 0x40000100, 0x40000100, 0x40000100, 0x40000100, - 0x02a19120, 0x40000100, -}; - -static const unsigned int d945gtp5_pin_configs[10] = { - 0x0221401f, 0x01011012, 0x01813024, 0x01014010, - 0x01a19021, 0x01016011, 0x01452130, 0x40000100, - 0x02a19320, 0x40000100, -}; - -static const unsigned int intel_mac_v1_pin_configs[10] = { - 0x0121e21f, 0x400000ff, 0x9017e110, 0x400000fd, - 0x400000fe, 0x0181e020, 0x1145e030, 0x11c5e240, - 0x400000fc, 0x400000fb, -}; - -static const unsigned int intel_mac_v2_pin_configs[10] = { - 0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd, - 0x400000fe, 0x0181e020, 0x1145e230, 0x500000fa, - 0x400000fc, 0x400000fb, -}; - -static const unsigned int intel_mac_v3_pin_configs[10] = { - 0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd, - 0x400000fe, 0x0181e020, 0x1145e230, 0x11c5e240, - 0x400000fc, 0x400000fb, -}; - -static const unsigned int intel_mac_v4_pin_configs[10] = { - 0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f, - 0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240, - 0x400000fc, 0x400000fb, -}; - -static const unsigned int intel_mac_v5_pin_configs[10] = { - 0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f, - 0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240, - 0x400000fc, 0x400000fb, -}; - -static const unsigned int ecs202_pin_configs[10] = { - 0x0221401f, 0x02a19020, 0x01a19020, 0x01114010, - 0x408000f0, 0x01813022, 0x074510a0, 0x40c400f1, - 0x9037012e, 0x40e000f2, -}; - -static const unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = { - [STAC_D945_REF] = ref922x_pin_configs, - [STAC_D945GTP3] = d945gtp3_pin_configs, - [STAC_D945GTP5] = d945gtp5_pin_configs, - [STAC_INTEL_MAC_V1] = intel_mac_v1_pin_configs, - [STAC_INTEL_MAC_V2] = intel_mac_v2_pin_configs, - [STAC_INTEL_MAC_V3] = intel_mac_v3_pin_configs, - [STAC_INTEL_MAC_V4] = intel_mac_v4_pin_configs, - [STAC_INTEL_MAC_V5] = intel_mac_v5_pin_configs, - [STAC_INTEL_MAC_AUTO] = intel_mac_v3_pin_configs, - /* for backward compatibility */ - [STAC_MACMINI] = intel_mac_v3_pin_configs, - [STAC_MACBOOK] = intel_mac_v5_pin_configs, - [STAC_MACBOOK_PRO_V1] = intel_mac_v3_pin_configs, - [STAC_MACBOOK_PRO_V2] = intel_mac_v3_pin_configs, - [STAC_IMAC_INTEL] = intel_mac_v2_pin_configs, - [STAC_IMAC_INTEL_20] = intel_mac_v3_pin_configs, - [STAC_ECS_202] = ecs202_pin_configs, - [STAC_922X_DELL_D81] = dell_922x_d81_pin_configs, - [STAC_922X_DELL_D82] = dell_922x_d82_pin_configs, - [STAC_922X_DELL_M81] = dell_922x_m81_pin_configs, - [STAC_922X_DELL_M82] = dell_922x_m82_pin_configs, -}; - -static const char * const stac922x_models[STAC_922X_MODELS] = { - [STAC_922X_AUTO] = "auto", - [STAC_D945_REF] = "ref", - [STAC_D945GTP5] = "5stack", - [STAC_D945GTP3] = "3stack", - [STAC_INTEL_MAC_V1] = "intel-mac-v1", - [STAC_INTEL_MAC_V2] = "intel-mac-v2", - [STAC_INTEL_MAC_V3] = "intel-mac-v3", - [STAC_INTEL_MAC_V4] = "intel-mac-v4", - [STAC_INTEL_MAC_V5] = "intel-mac-v5", - [STAC_INTEL_MAC_AUTO] = "intel-mac-auto", - /* for backward compatibility */ - [STAC_MACMINI] = "macmini", - [STAC_MACBOOK] = "macbook", - [STAC_MACBOOK_PRO_V1] = "macbook-pro-v1", - [STAC_MACBOOK_PRO_V2] = "macbook-pro", - [STAC_IMAC_INTEL] = "imac-intel", - [STAC_IMAC_INTEL_20] = "imac-intel-20", - [STAC_ECS_202] = "ecs202", - [STAC_922X_DELL_D81] = "dell-d81", - [STAC_922X_DELL_D82] = "dell-d82", - [STAC_922X_DELL_M81] = "dell-m81", - [STAC_922X_DELL_M82] = "dell-m82", -}; - -static const struct snd_pci_quirk stac922x_cfg_tbl[] = { - /* SigmaTel reference board */ - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, - "DFI LanParty", STAC_D945_REF), - SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, - "DFI LanParty", STAC_D945_REF), - /* Intel 945G based systems */ - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0101, - "Intel D945G", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0202, - "Intel D945G", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0606, - "Intel D945G", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0601, - "Intel D945G", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0111, - "Intel D945G", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1115, - "Intel D945G", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1116, - "Intel D945G", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1117, - "Intel D945G", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1118, - "Intel D945G", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1119, - "Intel D945G", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x8826, - "Intel D945G", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5049, - "Intel D945G", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5055, - "Intel D945G", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5048, - "Intel D945G", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0110, - "Intel D945G", STAC_D945GTP3), - /* Intel D945G 5-stack systems */ - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0404, - "Intel D945G", STAC_D945GTP5), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0303, - "Intel D945G", STAC_D945GTP5), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0013, - "Intel D945G", STAC_D945GTP5), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0417, - "Intel D945G", STAC_D945GTP5), - /* Intel 945P based systems */ - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0b0b, - "Intel D945P", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0112, - "Intel D945P", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0d0d, - "Intel D945P", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0909, - "Intel D945P", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0505, - "Intel D945P", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0707, - "Intel D945P", STAC_D945GTP5), - /* other intel */ - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0204, - "Intel D945", STAC_D945_REF), - /* other systems */ - /* Apple Intel Mac (Mac Mini, MacBook, MacBook Pro...) */ - SND_PCI_QUIRK(0x8384, 0x7680, - "Mac", STAC_INTEL_MAC_AUTO), - /* Dell systems */ - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a7, - "unknown Dell", STAC_922X_DELL_D81), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a9, - "unknown Dell", STAC_922X_DELL_D81), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ab, - "unknown Dell", STAC_922X_DELL_D81), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ac, - "unknown Dell", STAC_922X_DELL_D82), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01bf, - "unknown Dell", STAC_922X_DELL_M81), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d0, - "unknown Dell", STAC_922X_DELL_D82), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d1, - "unknown Dell", STAC_922X_DELL_D81), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d2, - "unknown Dell", STAC_922X_DELL_D81), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d7, - "Dell XPS M1210", STAC_922X_DELL_M82), - /* ECS/PC Chips boards */ - SND_PCI_QUIRK_MASK(0x1019, 0xf000, 0x2000, - "ECS/PC chips", STAC_ECS_202), - {} /* terminator */ -}; - -static const unsigned int ref927x_pin_configs[14] = { - 0x02214020, 0x02a19080, 0x0181304e, 0x01014010, - 0x01a19040, 0x01011012, 0x01016011, 0x0101201f, - 0x183301f0, 0x18a001f0, 0x18a001f0, 0x01442070, - 0x01c42190, 0x40000100, -}; - -static const unsigned int d965_3st_pin_configs[14] = { - 0x0221401f, 0x02a19120, 0x40000100, 0x01014011, - 0x01a19021, 0x01813024, 0x40000100, 0x40000100, - 0x40000100, 0x40000100, 0x40000100, 0x40000100, - 0x40000100, 0x40000100 -}; - -static const unsigned int d965_5st_pin_configs[14] = { - 0x02214020, 0x02a19080, 0x0181304e, 0x01014010, - 0x01a19040, 0x01011012, 0x01016011, 0x40000100, - 0x40000100, 0x40000100, 0x40000100, 0x01442070, - 0x40000100, 0x40000100 -}; - -static const unsigned int d965_5st_no_fp_pin_configs[14] = { - 0x40000100, 0x40000100, 0x0181304e, 0x01014010, - 0x01a19040, 0x01011012, 0x01016011, 0x40000100, - 0x40000100, 0x40000100, 0x40000100, 0x01442070, - 0x40000100, 0x40000100 -}; - -static const unsigned int dell_3st_pin_configs[14] = { - 0x02211230, 0x02a11220, 0x01a19040, 0x01114210, - 0x01111212, 0x01116211, 0x01813050, 0x01112214, - 0x403003fa, 0x90a60040, 0x90a60040, 0x404003fb, - 0x40c003fc, 0x40000100 -}; - -static const 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, - [STAC_D965_5ST_NO_FP] = d965_5st_no_fp_pin_configs, - [STAC_DELL_3ST] = dell_3st_pin_configs, - [STAC_DELL_BIOS] = NULL, - [STAC_927X_VOLKNOB] = NULL, -}; - -static const char * const stac927x_models[STAC_927X_MODELS] = { - [STAC_927X_AUTO] = "auto", - [STAC_D965_REF_NO_JD] = "ref-no-jd", - [STAC_D965_REF] = "ref", - [STAC_D965_3ST] = "3stack", - [STAC_D965_5ST] = "5stack", - [STAC_D965_5ST_NO_FP] = "5stack-no-fp", - [STAC_DELL_3ST] = "dell-3stack", - [STAC_DELL_BIOS] = "dell-bios", - [STAC_927X_VOLKNOB] = "volknob", -}; - -static const struct snd_pci_quirk stac927x_cfg_tbl[] = { - /* SigmaTel reference board */ - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, - "DFI LanParty", STAC_D965_REF), - SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, - "DFI LanParty", STAC_D965_REF), - /* Intel 946 based systems */ - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x3d01, "Intel D946", STAC_D965_3ST), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0xa301, "Intel D946", STAC_D965_3ST), - /* 965 based 3 stack systems */ - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2100, - "Intel D965", STAC_D965_3ST), - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2000, - "Intel D965", STAC_D965_3ST), - /* Dell 3 stack systems */ - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01dd, "Dell Dimension E520", STAC_DELL_3ST), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ed, "Dell ", STAC_DELL_3ST), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f4, "Dell ", STAC_DELL_3ST), - /* Dell 3 stack systems with verb table in BIOS */ - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f3, "Dell Inspiron 1420", STAC_DELL_BIOS), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f7, "Dell XPS M1730", STAC_DELL_BIOS), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0227, "Dell Vostro 1400 ", STAC_DELL_BIOS), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022e, "Dell ", STAC_DELL_BIOS), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022f, "Dell Inspiron 1525", STAC_DELL_BIOS), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0242, "Dell ", STAC_DELL_BIOS), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0243, "Dell ", STAC_DELL_BIOS), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ff, "Dell ", STAC_DELL_BIOS), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0209, "Dell XPS 1330", STAC_DELL_BIOS), - /* 965 based 5 stack systems */ - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2300, - "Intel D965", STAC_D965_5ST), - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2500, - "Intel D965", STAC_D965_5ST), - /* volume-knob fixes */ - SND_PCI_QUIRK_VENDOR(0x10cf, "FSC", STAC_927X_VOLKNOB), - {} /* terminator */ -}; - -static const unsigned int ref9205_pin_configs[12] = { - 0x40000100, 0x40000100, 0x01016011, 0x01014010, - 0x01813122, 0x01a19021, 0x01019020, 0x40000100, - 0x90a000f0, 0x90a000f0, 0x01441030, 0x01c41030 -}; - -/* - STAC 9205 pin configs for - 102801F1 - 102801F2 - 102801FC - 102801FD - 10280204 - 1028021F - 10280228 (Dell Vostro 1500) - 10280229 (Dell Vostro 1700) -*/ -static const unsigned int dell_9205_m42_pin_configs[12] = { - 0x0321101F, 0x03A11020, 0x400003FA, 0x90170310, - 0x400003FB, 0x400003FC, 0x400003FD, 0x40F000F9, - 0x90A60330, 0x400003FF, 0x0144131F, 0x40C003FE, -}; - -/* - STAC 9205 pin configs for - 102801F9 - 102801FA - 102801FE - 102801FF (Dell Precision M4300) - 10280206 - 10280200 - 10280201 -*/ -static const unsigned int dell_9205_m43_pin_configs[12] = { - 0x0321101f, 0x03a11020, 0x90a70330, 0x90170310, - 0x400000fe, 0x400000ff, 0x400000fd, 0x40f000f9, - 0x400000fa, 0x400000fc, 0x0144131f, 0x40c003f8, -}; - -static const unsigned int dell_9205_m44_pin_configs[12] = { - 0x0421101f, 0x04a11020, 0x400003fa, 0x90170310, - 0x400003fb, 0x400003fc, 0x400003fd, 0x400003f9, - 0x90a60330, 0x400003ff, 0x01441340, 0x40c003fe, -}; - -static const unsigned int *stac9205_brd_tbl[STAC_9205_MODELS] = { - [STAC_9205_REF] = ref9205_pin_configs, - [STAC_9205_DELL_M42] = dell_9205_m42_pin_configs, - [STAC_9205_DELL_M43] = dell_9205_m43_pin_configs, - [STAC_9205_DELL_M44] = dell_9205_m44_pin_configs, - [STAC_9205_EAPD] = NULL, -}; - -static const char * const stac9205_models[STAC_9205_MODELS] = { - [STAC_9205_AUTO] = "auto", - [STAC_9205_REF] = "ref", - [STAC_9205_DELL_M42] = "dell-m42", - [STAC_9205_DELL_M43] = "dell-m43", - [STAC_9205_DELL_M44] = "dell-m44", - [STAC_9205_EAPD] = "eapd", -}; - -static const struct snd_pci_quirk stac9205_cfg_tbl[] = { - /* SigmaTel reference board */ - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, - "DFI LanParty", STAC_9205_REF), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0xfb30, - "SigmaTel", STAC_9205_REF), - SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, - "DFI LanParty", STAC_9205_REF), - /* Dell */ - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f1, - "unknown Dell", STAC_9205_DELL_M42), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f2, - "unknown Dell", STAC_9205_DELL_M42), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f8, - "Dell Precision", STAC_9205_DELL_M43), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f9, - "Dell Precision", STAC_9205_DELL_M43), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fa, - "Dell Precision", STAC_9205_DELL_M43), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fc, - "unknown Dell", STAC_9205_DELL_M42), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fd, - "unknown Dell", STAC_9205_DELL_M42), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fe, - "Dell Precision", STAC_9205_DELL_M43), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ff, - "Dell Precision M4300", STAC_9205_DELL_M43), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0204, - "unknown Dell", STAC_9205_DELL_M42), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0206, - "Dell Precision", STAC_9205_DELL_M43), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021b, - "Dell Precision", STAC_9205_DELL_M43), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021c, - "Dell Precision", STAC_9205_DELL_M43), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021f, - "Dell Inspiron", STAC_9205_DELL_M44), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0228, - "Dell Vostro 1500", STAC_9205_DELL_M42), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0229, - "Dell Vostro 1700", STAC_9205_DELL_M42), - /* Gateway */ - SND_PCI_QUIRK(0x107b, 0x0560, "Gateway T6834c", STAC_9205_EAPD), - SND_PCI_QUIRK(0x107b, 0x0565, "Gateway T1616", STAC_9205_EAPD), - {} /* terminator */ -}; - -static void stac92xx_set_config_regs(struct hda_codec *codec, - const unsigned int *pincfgs) -{ - int i; - struct sigmatel_spec *spec = codec->spec; - - if (!pincfgs) - return; - - for (i = 0; i < spec->num_pins; i++) - if (spec->pin_nids[i] && pincfgs[i]) - snd_hda_codec_set_pincfg(codec, spec->pin_nids[i], - pincfgs[i]); -} - -/* - * Analog playback callbacks - */ -static int stac92xx_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct sigmatel_spec *spec = codec->spec; - if (spec->stream_delay) - msleep(spec->stream_delay); - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, - hinfo); -} - -static int stac92xx_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 sigmatel_spec *spec = codec->spec; - return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, format, substream); -} - -static int stac92xx_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct sigmatel_spec *spec = codec->spec; - return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); -} - -/* - * Digital playback callbacks - */ -static int stac92xx_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct sigmatel_spec *spec = codec->spec; - return snd_hda_multi_out_dig_open(codec, &spec->multiout); -} - -static int stac92xx_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct sigmatel_spec *spec = codec->spec; - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} - -static int stac92xx_dig_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 sigmatel_spec *spec = codec->spec; - return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, - stream_tag, format, substream); -} - -static int stac92xx_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct sigmatel_spec *spec = codec->spec; - return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); -} - - -/* - * Analog capture callbacks - */ -static int stac92xx_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct sigmatel_spec *spec = codec->spec; - hda_nid_t nid = spec->adc_nids[substream->number]; - - if (spec->powerdown_adcs) { - msleep(40); - 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); - return 0; -} - -static int stac92xx_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct sigmatel_spec *spec = codec->spec; - hda_nid_t nid = spec->adc_nids[substream->number]; - - snd_hda_codec_cleanup_stream(codec, nid); - if (spec->powerdown_adcs) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); - return 0; -} - -static const struct hda_pcm_stream stac92xx_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in stac92xx_build_pcms */ - .ops = { - .open = stac92xx_dig_playback_pcm_open, - .close = stac92xx_dig_playback_pcm_close, - .prepare = stac92xx_dig_playback_pcm_prepare, - .cleanup = stac92xx_dig_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream stac92xx_pcm_digital_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in stac92xx_build_pcms */ -}; - -static const struct hda_pcm_stream stac92xx_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 8, - .nid = 0x02, /* NID to query formats and rates */ - .ops = { - .open = stac92xx_playback_pcm_open, - .prepare = stac92xx_playback_pcm_prepare, - .cleanup = stac92xx_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream stac92xx_pcm_analog_alt_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .nid = 0x06, /* NID to query formats and rates */ - .ops = { - .open = stac92xx_playback_pcm_open, - .prepare = stac92xx_playback_pcm_prepare, - .cleanup = stac92xx_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream stac92xx_pcm_analog_capture = { - .channels_min = 2, - .channels_max = 2, - /* NID + .substreams is set in stac92xx_build_pcms */ - .ops = { - .prepare = stac92xx_capture_pcm_prepare, - .cleanup = stac92xx_capture_pcm_cleanup - }, -}; - -static int stac92xx_build_pcms(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; - - codec->num_pcms = 1; - codec->pcm_info = info; - - info->name = "STAC92xx Analog"; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_analog_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = - spec->multiout.dac_nids[0]; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_analog_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; - info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adcs; - - if (spec->alt_switch) { - codec->num_pcms++; - info++; - info->name = "STAC92xx Analog Alt"; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_analog_alt_playback; - } - - if (spec->multiout.dig_out_nid || spec->dig_in_nid) { - codec->num_pcms++; - info++; - info->name = "STAC92xx Digital"; - info->pcm_type = spec->autocfg.dig_out_type[0]; - if (spec->multiout.dig_out_nid) { - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_digital_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; - } - if (spec->dig_in_nid) { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_digital_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; - } - } - - return 0; -} - -static unsigned int stac92xx_get_default_vref(struct hda_codec *codec, - hda_nid_t nid) -{ - unsigned int pincap = snd_hda_query_pin_caps(codec, nid); - pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; - if (pincap & AC_PINCAP_VREF_100) - return AC_PINCTL_VREF_100; - if (pincap & AC_PINCAP_VREF_80) - return AC_PINCTL_VREF_80; - if (pincap & AC_PINCAP_VREF_50) - return AC_PINCTL_VREF_50; - if (pincap & AC_PINCAP_VREF_GRD) - return AC_PINCTL_VREF_GRD; - return 0; -} - -static void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int pin_type) - -{ - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type); -} - -#define stac92xx_hp_switch_info snd_ctl_boolean_mono_info - -static int stac92xx_hp_switch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - - ucontrol->value.integer.value[0] = !!spec->hp_switch; - return 0; -} - -static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid); - -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; - int nid = kcontrol->private_value; - - spec->hp_switch = ucontrol->value.integer.value[0] ? nid : 0; - - /* check to be sure that the ports are up to date with - * switch changes - */ - stac_issue_unsol_event(codec, nid); - - return 1; -} - -static int stac92xx_dc_bias_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - int i; - static const char * const texts[] = { - "Mic In", "Line In", "Line Out" - }; - - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - hda_nid_t nid = kcontrol->private_value; - - if (nid == spec->mic_switch || nid == spec->line_switch) - i = 3; - else - i = 2; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->value.enumerated.items = i; - uinfo->count = 1; - if (uinfo->value.enumerated.item >= i) - uinfo->value.enumerated.item = i-1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - - return 0; -} - -static int stac92xx_dc_bias_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value; - unsigned int vref = stac92xx_vref_get(codec, nid); - - if (vref == stac92xx_get_default_vref(codec, nid)) - ucontrol->value.enumerated.item[0] = 0; - else if (vref == AC_PINCTL_VREF_GRD) - ucontrol->value.enumerated.item[0] = 1; - else if (vref == AC_PINCTL_VREF_HIZ) - ucontrol->value.enumerated.item[0] = 2; - - return 0; -} - -static int stac92xx_dc_bias_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned int new_vref = 0; - int error; - hda_nid_t nid = kcontrol->private_value; - - if (ucontrol->value.enumerated.item[0] == 0) - new_vref = stac92xx_get_default_vref(codec, nid); - else if (ucontrol->value.enumerated.item[0] == 1) - new_vref = AC_PINCTL_VREF_GRD; - else if (ucontrol->value.enumerated.item[0] == 2) - new_vref = AC_PINCTL_VREF_HIZ; - else - return 0; - - if (new_vref != stac92xx_vref_get(codec, nid)) { - error = stac92xx_vref_set(codec, nid, new_vref); - return error; - } - - return 0; -} - -static int stac92xx_io_switch_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - char *texts[2]; - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - - if (kcontrol->private_value == spec->line_switch) - texts[0] = "Line In"; - else - texts[0] = "Mic In"; - texts[1] = "Line Out"; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->value.enumerated.items = 2; - uinfo->count = 1; - - if (uinfo->value.enumerated.item >= 2) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - - return 0; -} - -static int stac92xx_io_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - hda_nid_t nid = kcontrol->private_value; - int io_idx = (nid == spec->mic_switch) ? 1 : 0; - - ucontrol->value.enumerated.item[0] = spec->io_switch[io_idx]; - return 0; -} - -static int stac92xx_io_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; - hda_nid_t nid = kcontrol->private_value; - int io_idx = (nid == spec->mic_switch) ? 1 : 0; - unsigned short val = !!ucontrol->value.enumerated.item[0]; - - spec->io_switch[io_idx] = val; - - if (val) - stac92xx_auto_set_pinctl(codec, nid, AC_PINCTL_OUT_EN); - else { - unsigned int pinctl = AC_PINCTL_IN_EN; - if (io_idx) /* set VREF for mic */ - pinctl |= stac92xx_get_default_vref(codec, nid); - stac92xx_auto_set_pinctl(codec, nid, pinctl); - } - - /* check the auto-mute again: we need to mute/unmute the speaker - * appropriately according to the pin direction - */ - if (spec->hp_detect) - stac_issue_unsol_event(codec, nid); - - return 1; -} - -#define stac92xx_clfe_switch_info snd_ctl_boolean_mono_info - -static int stac92xx_clfe_switch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - - ucontrol->value.integer.value[0] = spec->clfe_swap; - return 0; -} - -static int stac92xx_clfe_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; - hda_nid_t nid = kcontrol->private_value & 0xff; - unsigned int val = !!ucontrol->value.integer.value[0]; - - if (spec->clfe_swap == val) - return 0; - - spec->clfe_swap = val; - - snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE, - spec->clfe_swap ? 0x4 : 0x0); - - return 1; -} - -#define STAC_CODEC_HP_SWITCH(xname) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = 0, \ - .info = stac92xx_hp_switch_info, \ - .get = stac92xx_hp_switch_get, \ - .put = stac92xx_hp_switch_put, \ - } - -#define STAC_CODEC_IO_SWITCH(xname, xpval) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = 0, \ - .info = stac92xx_io_switch_info, \ - .get = stac92xx_io_switch_get, \ - .put = stac92xx_io_switch_put, \ - .private_value = xpval, \ - } - -#define STAC_CODEC_CLFE_SWITCH(xname, xpval) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = 0, \ - .info = stac92xx_clfe_switch_info, \ - .get = stac92xx_clfe_switch_get, \ - .put = stac92xx_clfe_switch_put, \ - .private_value = xpval, \ - } - -enum { - STAC_CTL_WIDGET_VOL, - STAC_CTL_WIDGET_MUTE, - STAC_CTL_WIDGET_MUTE_BEEP, - STAC_CTL_WIDGET_MONO_MUX, - STAC_CTL_WIDGET_HP_SWITCH, - STAC_CTL_WIDGET_IO_SWITCH, - STAC_CTL_WIDGET_CLFE_SWITCH, - STAC_CTL_WIDGET_DC_BIAS -}; - -static const struct snd_kcontrol_new stac92xx_control_templates[] = { - HDA_CODEC_VOLUME(NULL, 0, 0, 0), - HDA_CODEC_MUTE(NULL, 0, 0, 0), - HDA_CODEC_MUTE_BEEP(NULL, 0, 0, 0), - STAC_MONO_MUX, - STAC_CODEC_HP_SWITCH(NULL), - STAC_CODEC_IO_SWITCH(NULL, 0), - STAC_CODEC_CLFE_SWITCH(NULL, 0), - DC_BIAS(NULL, 0, 0), -}; - -/* add dynamic controls */ -static struct snd_kcontrol_new * -stac_control_new(struct sigmatel_spec *spec, - const struct snd_kcontrol_new *ktemp, - const char *name, - unsigned int subdev) -{ - struct snd_kcontrol_new *knew; - - snd_array_init(&spec->kctls, sizeof(*knew), 32); - knew = snd_array_new(&spec->kctls); - if (!knew) - return NULL; - *knew = *ktemp; - knew->name = kstrdup(name, GFP_KERNEL); - if (!knew->name) { - /* roolback */ - memset(knew, 0, sizeof(*knew)); - spec->kctls.alloced--; - return NULL; - } - knew->subdevice = subdev; - return knew; -} - -static int stac92xx_add_control_temp(struct sigmatel_spec *spec, - const struct snd_kcontrol_new *ktemp, - int idx, const char *name, - unsigned long val) -{ - struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name, - HDA_SUBDEV_AMP_FLAG); - if (!knew) - return -ENOMEM; - knew->index = idx; - knew->private_value = val; - 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 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); -} - -static const struct snd_kcontrol_new stac_input_src_temp = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Input Source", - .info = stac92xx_mux_enum_info, - .get = stac92xx_mux_enum_get, - .put = stac92xx_mux_enum_put, -}; - -static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec, - hda_nid_t nid, int idx) -{ - int def_conf = snd_hda_codec_get_pincfg(codec, nid); - int control = 0; - struct sigmatel_spec *spec = codec->spec; - char name[22]; - - if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) { - if (stac92xx_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD - && nid == spec->line_switch) - control = STAC_CTL_WIDGET_IO_SWITCH; - else if (snd_hda_query_pin_caps(codec, nid) - & (AC_PINCAP_VREF_GRD << AC_PINCAP_VREF_SHIFT)) - control = STAC_CTL_WIDGET_DC_BIAS; - else if (nid == spec->mic_switch) - control = STAC_CTL_WIDGET_IO_SWITCH; - } - - if (control) { - snd_hda_get_pin_label(codec, nid, &spec->autocfg, - name, sizeof(name), NULL); - return stac92xx_add_control(codec->spec, control, - strcat(name, " Jack Mode"), nid); - } - - return 0; -} - -static int stac92xx_add_input_source(struct sigmatel_spec *spec) -{ - struct snd_kcontrol_new *knew; - struct hda_input_mux *imux = &spec->private_imux; - - if (spec->auto_mic) - return 0; /* no need for input source */ - if (!spec->num_adcs || imux->num_items <= 1) - return 0; /* no need for input source control */ - knew = stac_control_new(spec, &stac_input_src_temp, - stac_input_src_temp.name, 0); - if (!knew) - return -ENOMEM; - knew->count = spec->num_adcs; - return 0; -} - -/* 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; - struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t nid; - unsigned int pincap; - int i; - - if (cfg->line_out_type != AUTO_PIN_LINE_OUT) - return 0; - for (i = 0; i < cfg->num_inputs; i++) { - if (cfg->inputs[i].type == AUTO_PIN_LINE_IN) { - nid = cfg->inputs[i].pin; - pincap = snd_hda_query_pin_caps(codec, nid); - if (pincap & AC_PINCAP_OUT) - return nid; - } - } - return 0; -} - -static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t nid); - -/* check whether the mic-input can be used as line-out */ -static hda_nid_t check_mic_out_switch(struct hda_codec *codec, hda_nid_t *dac) -{ - struct sigmatel_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int def_conf, pincap; - int i; - - *dac = 0; - if (cfg->line_out_type != AUTO_PIN_LINE_OUT) - return 0; - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - if (cfg->inputs[i].type != AUTO_PIN_MIC) - continue; - def_conf = snd_hda_codec_get_pincfg(codec, nid); - /* some laptops have an internal analog microphone - * which can't be used as a output */ - if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) { - pincap = snd_hda_query_pin_caps(codec, nid); - if (pincap & AC_PINCAP_OUT) { - *dac = get_unassigned_dac(codec, nid); - if (*dac) - return nid; - } - } - } - return 0; -} - -static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid) -{ - int i; - - for (i = 0; i < spec->multiout.num_dacs; i++) { - if (spec->multiout.dac_nids[i] == nid) - return 1; - } - - 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; - struct auto_pin_cfg *cfg = &spec->autocfg; - int j, conn_len; - hda_nid_t conn[HDA_MAX_CONNECTIONS], fallback_dac; - unsigned int wcaps, wtype; - - conn_len = snd_hda_get_connections(codec, nid, conn, - HDA_MAX_CONNECTIONS); - /* 92HD88: trace back up the link of nids to find the DAC */ - while (conn_len == 1 && (get_wcaps_type(get_wcaps(codec, conn[0])) - != AC_WID_AUD_OUT)) { - nid = conn[0]; - conn_len = snd_hda_get_connections(codec, nid, conn, - HDA_MAX_CONNECTIONS); - } - for (j = 0; j < conn_len; j++) { - wcaps = get_wcaps(codec, conn[j]); - wtype = get_wcaps_type(wcaps); - /* 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]; - } - } - - /* if all DACs are already assigned, connect to the primary DAC, - unless we're assigning a secondary headphone */ - fallback_dac = spec->multiout.dac_nids[0]; - if (spec->multiout.hp_nid) { - for (j = 0; j < cfg->hp_outs; j++) - if (cfg->hp_pins[j] == nid) { - fallback_dac = spec->multiout.hp_nid; - break; - } - } - - if (conn_len > 1) { - for (j = 0; j < conn_len; j++) { - if (conn[j] == fallback_dac) { - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, j); - break; - } - } - } - 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[] - * contains atleast one DAC in its connection list. Some 92xx - * 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 sigmatel_spec *spec = codec->spec; - 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]; - 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 - */ - cfg->line_outs = spec->multiout.num_dacs; - break; - } - /* error out, no available DAC found */ - snd_printk(KERN_ERR - "%s: No available DAC for pin 0x%x\n", - __func__, nid); - return -ENODEV; - } - 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; - } - - /* 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, &dac); - if (nid && 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); - } - - 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; -} - -/* create volume control/switch for the given prefx type */ -static int create_controls_idx(struct hda_codec *codec, const char *pfx, - int idx, hda_nid_t nid, int chs) -{ - struct sigmatel_spec *spec = codec->spec; - char name[32]; - int err; - - if (!spec->check_volume_offset) { - unsigned int caps, step, nums, db_scale; - caps = query_amp_caps(codec, nid, HDA_OUTPUT); - step = (caps & AC_AMPCAP_STEP_SIZE) >> - AC_AMPCAP_STEP_SIZE_SHIFT; - step = (step + 1) * 25; /* in .01dB unit */ - nums = (caps & AC_AMPCAP_NUM_STEPS) >> - AC_AMPCAP_NUM_STEPS_SHIFT; - db_scale = nums * step; - /* if dB scale is over -64dB, and finer enough, - * let's reduce it to half - */ - if (db_scale > 6400 && nums >= 0x1f) - spec->volume_offset = nums / 2; - spec->check_volume_offset = 1; - } - - sprintf(name, "%s Playback Volume", pfx); - err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_VOL, idx, name, - HDA_COMPOSE_AMP_VAL_OFS(nid, chs, 0, HDA_OUTPUT, - spec->volume_offset)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", pfx); - err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_MUTE, idx, name, - HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); - if (err < 0) - return err; - return 0; -} - -#define create_controls(codec, pfx, nid, chs) \ - create_controls_idx(codec, pfx, 0, nid, chs) - -static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid) -{ - if (spec->multiout.num_dacs > 4) { - printk(KERN_WARNING "stac92xx: No space for DAC 0x%x\n", nid); - return 1; - } else { - snd_BUG_ON(spec->multiout.dac_nids != spec->dac_nids); - spec->dac_nids[spec->multiout.num_dacs] = nid; - spec->multiout.num_dacs++; - } - return 0; -} - -static int add_spec_extra_dacs(struct sigmatel_spec *spec, hda_nid_t nid) -{ - 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; -} - -/* Create output controls - * The mixer elements are named depending on the given type (AUTO_PIN_XXX_OUT) - */ -static int create_multi_out_ctls(struct hda_codec *codec, int num_outs, - const hda_nid_t *pins, - const hda_nid_t *dac_nids, - int type) -{ - struct sigmatel_spec *spec = codec->spec; - static const char * const chname[4] = { - "Front", "Surround", NULL /*CLFE*/, "Side" - }; - hda_nid_t nid; - int i, err; - unsigned int wid_caps; - - for (i = 0; i < num_outs && i < ARRAY_SIZE(chname); i++) { - if (type == AUTO_PIN_HP_OUT && !spec->hp_detect) { - if (is_jack_detectable(codec, pins[i])) - spec->hp_detect = 1; - } - nid = dac_nids[i]; - if (!nid) - continue; - if (type != AUTO_PIN_HP_OUT && i == 2) { - /* Center/LFE */ - err = create_controls(codec, "Center", nid, 1); - if (err < 0) - return err; - err = create_controls(codec, "LFE", nid, 2); - if (err < 0) - return err; - - wid_caps = get_wcaps(codec, nid); - - if (wid_caps & AC_WCAP_LR_SWAP) { - err = stac92xx_add_control(spec, - STAC_CTL_WIDGET_CLFE_SWITCH, - "Swap Center/LFE Playback Switch", nid); - - if (err < 0) - return err; - } - - } else { - const char *name; - int idx; - switch (type) { - case AUTO_PIN_HP_OUT: - name = "Headphone"; - idx = i; - break; - case AUTO_PIN_SPEAKER_OUT: - name = "Speaker"; - idx = i; - break; - default: - name = chname[i]; - idx = 0; - break; - } - err = create_controls_idx(codec, name, idx, nid, 3); - if (err < 0) - return err; - } - } - return 0; -} - -static int stac92xx_add_capvol_ctls(struct hda_codec *codec, unsigned long vol, - unsigned long sw, int idx) -{ - int err; - err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx, - "Capture Volume", vol); - if (err < 0) - return err; - err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_MUTE, idx, - "Capture Switch", sw); - if (err < 0) - return err; - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - struct sigmatel_spec *spec = codec->spec; - hda_nid_t nid; - int err; - int idx; - - err = create_multi_out_ctls(codec, cfg->line_outs, cfg->line_out_pins, - spec->multiout.dac_nids, - cfg->line_out_type); - if (err < 0) - return err; - - 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", - cfg->hp_pins[cfg->hp_outs - 1]); - if (err < 0) - return err; - } - - for (idx = 0; idx < cfg->num_inputs; idx++) { - if (cfg->inputs[idx].type > AUTO_PIN_LINE_IN) - break; - nid = cfg->inputs[idx].pin; - err = stac92xx_add_jack_mode_control(codec, nid, idx); - if (err < 0) - return err; - } - - return 0; -} - -/* add playback controls for Speaker and HP outputs */ -static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec, - struct auto_pin_cfg *cfg) -{ - struct sigmatel_spec *spec = codec->spec; - int err; - - err = create_multi_out_ctls(codec, cfg->hp_outs, cfg->hp_pins, - spec->hp_dacs, AUTO_PIN_HP_OUT); - if (err < 0) - return err; - - err = create_multi_out_ctls(codec, cfg->speaker_outs, cfg->speaker_pins, - spec->speaker_dacs, AUTO_PIN_SPEAKER_OUT); - if (err < 0) - return err; - - return 0; -} - -/* labels for mono mux outputs */ -static const char * const stac92xx_mono_labels[4] = { - "DAC0", "DAC1", "Mixer", "DAC2" -}; - -/* create mono mux for mono out on capable codecs */ -static int stac92xx_auto_create_mono_output_ctls(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - struct hda_input_mux *mono_mux = &spec->private_mono_mux; - int i, num_cons; - hda_nid_t con_lst[ARRAY_SIZE(stac92xx_mono_labels)]; - - num_cons = snd_hda_get_connections(codec, - spec->mono_nid, - con_lst, - HDA_MAX_NUM_INPUTS); - if (num_cons <= 0 || num_cons > ARRAY_SIZE(stac92xx_mono_labels)) - return -EINVAL; - - for (i = 0; i < num_cons; i++) - snd_hda_add_imux_item(mono_mux, stac92xx_mono_labels[i], i, - NULL); - - return stac92xx_add_control(spec, STAC_CTL_WIDGET_MONO_MUX, - "Mono Mux", spec->mono_nid); -} - -/* create PC beep volume controls */ -static int stac92xx_auto_create_beep_ctls(struct hda_codec *codec, - hda_nid_t nid) -{ - struct sigmatel_spec *spec = codec->spec; - u32 caps = query_amp_caps(codec, nid, HDA_OUTPUT); - int err, type = STAC_CTL_WIDGET_MUTE_BEEP; - - if (spec->anabeep_nid == nid) - type = STAC_CTL_WIDGET_MUTE; - - /* check for mute support for the the amp */ - if ((caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT) { - err = stac92xx_add_control(spec, type, - "Beep Playback Switch", - HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT)); - if (err < 0) - return err; - } - - /* check to see if there is volume support for the amp */ - if ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) { - err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, - "Beep Playback Volume", - HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT)); - if (err < 0) - return err; - } - 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); - return snd_hda_enable_beep_device(codec, ucontrol->value.integer.value[0]); -} - -static const 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, "Beep Playback Switch", 0); -} -#endif - -static int stac92xx_auto_create_mux_input_ctls(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - int i, j, err = 0; - - for (i = 0; i < spec->num_muxes; i++) { - hda_nid_t nid; - unsigned int wcaps; - unsigned long val; - - nid = spec->mux_nids[i]; - wcaps = get_wcaps(codec, nid); - if (!(wcaps & AC_WCAP_OUT_AMP)) - continue; - - /* check whether already the same control was created as - * normal Capture Volume. - */ - val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - for (j = 0; j < spec->num_caps; j++) { - if (spec->capvols[j] == val) - break; - } - if (j < spec->num_caps) - continue; - - err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_VOL, i, - "Mux Capture Volume", val); - if (err < 0) - return err; - } - return 0; -}; - -static const char * const stac92xx_spdif_labels[3] = { - "Digital Playback", "Analog Mux 1", "Analog Mux 2", -}; - -static int stac92xx_auto_create_spdif_mux_ctls(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - struct hda_input_mux *spdif_mux = &spec->private_smux; - const char * const *labels = spec->spdif_labels; - int i, num_cons; - hda_nid_t con_lst[HDA_MAX_NUM_INPUTS]; - - num_cons = snd_hda_get_connections(codec, - spec->smux_nids[0], - con_lst, - HDA_MAX_NUM_INPUTS); - if (num_cons <= 0) - return -EINVAL; - - if (!labels) - labels = stac92xx_spdif_labels; - - for (i = 0; i < num_cons; i++) - snd_hda_add_imux_item(spdif_mux, labels[i], i, NULL); - - return 0; -} - -/* labels for dmic mux inputs */ -static const char * const stac92xx_dmic_labels[5] = { - "Analog Inputs", "Digital Mic 1", "Digital Mic 2", - "Digital Mic 3", "Digital Mic 4" -}; - -static hda_nid_t get_connected_node(struct hda_codec *codec, hda_nid_t mux, - int idx) -{ - hda_nid_t conn[HDA_MAX_NUM_INPUTS]; - int nums; - nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); - if (idx >= 0 && idx < nums) - return conn[idx]; - return 0; -} - -/* look for NID recursively */ -#define get_connection_index(codec, mux, nid) \ - snd_hda_get_conn_index(codec, mux, nid, 1) - -/* create a volume assigned to the given pin (only if supported) */ -/* return 1 if the volume control is created */ -static int create_elem_capture_vol(struct hda_codec *codec, hda_nid_t nid, - const char *label, int idx, int direction) -{ - unsigned int caps, nums; - char name[32]; - int err; - - if (direction == HDA_OUTPUT) - caps = AC_WCAP_OUT_AMP; - else - caps = AC_WCAP_IN_AMP; - if (!(get_wcaps(codec, nid) & caps)) - return 0; - caps = query_amp_caps(codec, nid, direction); - nums = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; - if (!nums) - return 0; - snprintf(name, sizeof(name), "%s Capture Volume", label); - err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, direction)); - if (err < 0) - return err; - return 1; -} - -/* create playback/capture controls for input pins on dmic capable codecs */ -static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - struct sigmatel_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux; - struct hda_input_mux *dimux = &spec->private_dimux; - int err, i; - unsigned int def_conf; - - snd_hda_add_imux_item(dimux, stac92xx_dmic_labels[0], 0, NULL); - - for (i = 0; i < spec->num_dmics; i++) { - hda_nid_t nid; - int index, type_idx; - char label[32]; - - nid = spec->dmic_nids[i]; - if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) - continue; - def_conf = snd_hda_codec_get_pincfg(codec, nid); - if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) - continue; - - index = get_connection_index(codec, spec->dmux_nids[0], nid); - if (index < 0) - continue; - - snd_hda_get_pin_label(codec, nid, &spec->autocfg, - label, sizeof(label), NULL); - snd_hda_add_imux_item(dimux, label, index, &type_idx); - if (snd_hda_get_bool_hint(codec, "separate_dmux") != 1) - snd_hda_add_imux_item(imux, label, index, &type_idx); - - err = create_elem_capture_vol(codec, nid, label, type_idx, - HDA_INPUT); - if (err < 0) - return err; - if (!err) { - err = create_elem_capture_vol(codec, nid, label, - type_idx, HDA_OUTPUT); - if (err < 0) - return err; - if (!err) { - nid = get_connected_node(codec, - spec->dmux_nids[0], index); - if (nid) - err = create_elem_capture_vol(codec, - nid, label, - type_idx, HDA_INPUT); - if (err < 0) - return err; - } - } - } - - return 0; -} - -static int check_mic_pin(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t *fixed, hda_nid_t *ext, hda_nid_t *dock) -{ - unsigned int cfg; - unsigned int type; - - if (!nid) - return 0; - cfg = snd_hda_codec_get_pincfg(codec, nid); - type = get_defcfg_device(cfg); - switch (snd_hda_get_input_pin_attr(cfg)) { - case INPUT_PIN_ATTR_INT: - if (*fixed) - return 1; /* already occupied */ - if (type != AC_JACK_MIC_IN) - return 1; /* invalid type */ - *fixed = nid; - break; - case INPUT_PIN_ATTR_UNUSED: - break; - case INPUT_PIN_ATTR_DOCK: - if (*dock) - return 1; /* already occupied */ - if (type != AC_JACK_MIC_IN && type != AC_JACK_LINE_IN) - return 1; /* invalid type */ - *dock = nid; - break; - default: - if (*ext) - return 1; /* already occupied */ - if (type != AC_JACK_MIC_IN) - return 1; /* invalid type */ - *ext = nid; - break; - } - return 0; -} - -static int set_mic_route(struct hda_codec *codec, - struct sigmatel_mic_route *mic, - hda_nid_t pin) -{ - struct sigmatel_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - mic->pin = pin; - if (pin == 0) - return 0; - for (i = 0; i < cfg->num_inputs; i++) { - if (pin == cfg->inputs[i].pin) - break; - } - if (i < cfg->num_inputs && cfg->inputs[i].type == AUTO_PIN_MIC) { - /* analog pin */ - i = get_connection_index(codec, spec->mux_nids[0], pin); - if (i < 0) - return -1; - mic->mux_idx = i; - mic->dmux_idx = -1; - if (spec->dmux_nids) - mic->dmux_idx = get_connection_index(codec, - spec->dmux_nids[0], - spec->mux_nids[0]); - } else if (spec->dmux_nids) { - /* digital pin */ - i = get_connection_index(codec, spec->dmux_nids[0], pin); - if (i < 0) - return -1; - mic->dmux_idx = i; - mic->mux_idx = -1; - if (spec->mux_nids) - mic->mux_idx = get_connection_index(codec, - spec->mux_nids[0], - spec->dmux_nids[0]); - } - return 0; -} - -/* return non-zero if the device is for automatic mic switch */ -static int stac_check_auto_mic(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t fixed, ext, dock; - int i; - - fixed = ext = dock = 0; - for (i = 0; i < cfg->num_inputs; i++) - if (check_mic_pin(codec, cfg->inputs[i].pin, - &fixed, &ext, &dock)) - return 0; - for (i = 0; i < spec->num_dmics; i++) - if (check_mic_pin(codec, spec->dmic_nids[i], - &fixed, &ext, &dock)) - return 0; - if (!fixed || (!ext && !dock)) - return 0; /* no input to switch */ - if (!is_jack_detectable(codec, ext)) - return 0; /* no unsol support */ - if (set_mic_route(codec, &spec->ext_mic, ext) || - set_mic_route(codec, &spec->int_mic, fixed) || - set_mic_route(codec, &spec->dock_mic, dock)) - return 0; /* something is wrong */ - return 1; -} - -/* create playback/capture controls for input pins */ -static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) -{ - struct sigmatel_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux; - int i, j; - const char *label; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - int index, err, type_idx; - - index = -1; - for (j = 0; j < spec->num_muxes; j++) { - index = get_connection_index(codec, spec->mux_nids[j], - nid); - if (index >= 0) - break; - } - if (index < 0) - continue; - - label = hda_get_autocfg_input_label(codec, cfg, i); - snd_hda_add_imux_item(imux, label, index, &type_idx); - - err = create_elem_capture_vol(codec, nid, - label, type_idx, - HDA_INPUT); - if (err < 0) - return err; - } - spec->num_analog_muxes = imux->num_items; - - if (imux->num_items) { - /* - * Set the current input for the muxes. - * The STAC9221 has two input muxes with identical source - * NID lists. Hopefully this won't get confused. - */ - for (i = 0; i < spec->num_muxes; i++) { - snd_hda_codec_write_cache(codec, spec->mux_nids[i], 0, - AC_VERB_SET_CONNECT_SEL, - imux->items[0].index); - } - } - - return 0; -} - -static void stac92xx_auto_init_multi_out(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->autocfg.line_outs; i++) { - hda_nid_t nid = spec->autocfg.line_out_pins[i]; - stac92xx_auto_set_pinctl(codec, nid, AC_PINCTL_OUT_EN); - } -} - -static void stac92xx_auto_init_hp_out(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->autocfg.hp_outs; i++) { - hda_nid_t pin; - pin = spec->autocfg.hp_pins[i]; - if (pin) /* connect to front */ - stac92xx_auto_set_pinctl(codec, pin, AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN); - } - for (i = 0; i < spec->autocfg.speaker_outs; i++) { - hda_nid_t pin; - pin = spec->autocfg.speaker_pins[i]; - if (pin) /* connect to front */ - stac92xx_auto_set_pinctl(codec, pin, AC_PINCTL_OUT_EN); - } -} - -static int is_dual_headphones(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - int i, valid_hps; - - if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT || - spec->autocfg.hp_outs <= 1) - return 0; - valid_hps = 0; - for (i = 0; i < spec->autocfg.hp_outs; i++) { - hda_nid_t nid = spec->autocfg.hp_pins[i]; - unsigned int cfg = snd_hda_codec_get_pincfg(codec, nid); - if (get_defcfg_location(cfg) & AC_JACK_LOC_SEPARATE) - continue; - valid_hps++; - } - return (valid_hps > 1); -} - - -static int stac92xx_parse_auto_config(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - hda_nid_t dig_out = 0, dig_in = 0; - int hp_swap = 0; - int i, err; - - if ((err = snd_hda_parse_pin_def_config(codec, - &spec->autocfg, - spec->dmic_nids)) < 0) - return err; - if (! spec->autocfg.line_outs) - return 0; /* can't find valid pin config */ - - /* If we have no real line-out pin and multiple hp-outs, HPs should - * be set up as multi-channel outputs. - */ - if (is_dual_headphones(codec)) { - /* Copy hp_outs to line_outs, backup line_outs in - * speaker_outs so that the following routines can handle - * HP pins as primary outputs. - */ - 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; - spec->autocfg.line_out_type = AUTO_PIN_HP_OUT; - spec->autocfg.hp_outs = 0; - hp_swap = 1; - } - if (spec->autocfg.mono_out_pin) { - int dir = get_wcaps(codec, spec->autocfg.mono_out_pin) & - (AC_WCAP_OUT_AMP | AC_WCAP_IN_AMP); - u32 caps = query_amp_caps(codec, - spec->autocfg.mono_out_pin, dir); - hda_nid_t conn_list[1]; - - /* get the mixer node and then the mono mux if it exists */ - if (snd_hda_get_connections(codec, - spec->autocfg.mono_out_pin, conn_list, 1) && - snd_hda_get_connections(codec, conn_list[0], - conn_list, 1) > 0) { - - int wcaps = get_wcaps(codec, conn_list[0]); - int wid_type = get_wcaps_type(wcaps); - /* LR swap check, some stac925x have a mux that - * changes the DACs output path instead of the - * mono-mux path. - */ - if (wid_type == AC_WID_AUD_SEL && - !(wcaps & AC_WCAP_LR_SWAP)) - spec->mono_nid = conn_list[0]; - } - if (dir) { - hda_nid_t nid = spec->autocfg.mono_out_pin; - - /* most mono outs have a least a mute/unmute switch */ - dir = (dir & AC_WCAP_OUT_AMP) ? HDA_OUTPUT : HDA_INPUT; - err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, - "Mono Playback Switch", - HDA_COMPOSE_AMP_VAL(nid, 1, 0, dir)); - if (err < 0) - return err; - /* check for volume support for the amp */ - if ((caps & AC_AMPCAP_NUM_STEPS) - >> AC_AMPCAP_NUM_STEPS_SHIFT) { - err = stac92xx_add_control(spec, - STAC_CTL_WIDGET_VOL, - "Mono Playback Volume", - HDA_COMPOSE_AMP_VAL(nid, 1, 0, dir)); - if (err < 0) - return err; - } - } - - stac92xx_auto_set_pinctl(codec, spec->autocfg.mono_out_pin, - AC_PINCTL_OUT_EN); - } - - 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); - if (err < 0) - return err; - } - - /* setup analog beep controls */ - if (spec->anabeep_nid > 0) { - err = stac92xx_auto_create_beep_ctls(codec, - spec->anabeep_nid); - if (err < 0) - return err; - } - - /* setup digital beep controls and input device */ -#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) - return err; - err = snd_hda_attach_beep_device(codec, nid); - if (err < 0) - return err; - if (codec->beep) { - /* IDT/STAC codecs have linear beep tone parameter */ - codec->beep->linear_tone = spec->linear_tone_beep; - /* if no beep switch is available, make its own one */ - caps = query_amp_caps(codec, nid, HDA_OUTPUT); - if (!(caps & AC_AMPCAP_MUTE)) { - err = stac92xx_beep_switch_ctl(codec); - if (err < 0) - return err; - } - } - } -#endif - - err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - /* All output parsing done, now restore the swapped hp pins */ - if (hp_swap) { - memcpy(spec->autocfg.hp_pins, spec->autocfg.line_out_pins, - sizeof(spec->autocfg.hp_pins)); - spec->autocfg.hp_outs = spec->autocfg.line_outs; - spec->autocfg.line_out_type = AUTO_PIN_HP_OUT; - spec->autocfg.line_outs = 0; - } - - if (stac_check_auto_mic(codec)) { - spec->auto_mic = 1; - /* only one capture for auto-mic */ - spec->num_adcs = 1; - spec->num_caps = 1; - spec->num_muxes = 1; - } - - for (i = 0; i < spec->num_caps; i++) { - err = stac92xx_add_capvol_ctls(codec, spec->capvols[i], - spec->capsws[i], i); - if (err < 0) - return err; - } - - err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - if (spec->mono_nid > 0) { - err = stac92xx_auto_create_mono_output_ctls(codec); - if (err < 0) - return err; - } - if (spec->num_dmics > 0 && !spec->dinput_mux) - if ((err = stac92xx_auto_create_dmic_input_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->num_smuxes > 0) { - err = stac92xx_auto_create_spdif_mux_ctls(codec); - if (err < 0) - return err; - } - - err = stac92xx_add_input_source(spec); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - if (spec->multiout.max_channels > 2) - spec->surr_switch = 1; - - /* find digital out and in converters */ - for (i = codec->start_nid; i < codec->start_nid + codec->num_nodes; i++) { - unsigned int wid_caps = get_wcaps(codec, i); - if (wid_caps & AC_WCAP_DIGITAL) { - switch (get_wcaps_type(wid_caps)) { - case AC_WID_AUD_OUT: - if (!dig_out) - dig_out = i; - break; - case AC_WID_AUD_IN: - if (!dig_in) - dig_in = i; - break; - } - } - } - if (spec->autocfg.dig_outs) - spec->multiout.dig_out_nid = dig_out; - if (dig_in && spec->autocfg.dig_in_pin) - spec->dig_in_nid = dig_in; - - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; - - spec->input_mux = &spec->private_imux; - if (!spec->dinput_mux) - spec->dinput_mux = &spec->private_dimux; - spec->sinput_mux = &spec->private_smux; - spec->mono_mux = &spec->private_mono_mux; - return 1; -} - -/* add playback controls for HP output */ -static int stac9200_auto_create_hp_ctls(struct hda_codec *codec, - struct auto_pin_cfg *cfg) -{ - struct sigmatel_spec *spec = codec->spec; - hda_nid_t pin = cfg->hp_pins[0]; - - if (! pin) - return 0; - - if (is_jack_detectable(codec, pin)) - spec->hp_detect = 1; - - return 0; -} - -/* add playback controls for LFE output */ -static int stac9200_auto_create_lfe_ctls(struct hda_codec *codec, - struct auto_pin_cfg *cfg) -{ - struct sigmatel_spec *spec = codec->spec; - int err; - hda_nid_t lfe_pin = 0x0; - int i; - - /* - * search speaker outs and line outs for a mono speaker pin - * with an amp. If one is found, add LFE controls - * for it. - */ - for (i = 0; i < spec->autocfg.speaker_outs && lfe_pin == 0x0; i++) { - hda_nid_t pin = spec->autocfg.speaker_pins[i]; - unsigned int wcaps = get_wcaps(codec, pin); - wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP); - if (wcaps == AC_WCAP_OUT_AMP) - /* found a mono speaker with an amp, must be lfe */ - lfe_pin = pin; - } - - /* if speaker_outs is 0, then speakers may be in line_outs */ - if (lfe_pin == 0 && spec->autocfg.speaker_outs == 0) { - for (i = 0; i < spec->autocfg.line_outs && lfe_pin == 0x0; i++) { - hda_nid_t pin = spec->autocfg.line_out_pins[i]; - unsigned int defcfg; - defcfg = snd_hda_codec_get_pincfg(codec, pin); - if (get_defcfg_device(defcfg) == AC_JACK_SPEAKER) { - unsigned int wcaps = get_wcaps(codec, pin); - wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP); - if (wcaps == AC_WCAP_OUT_AMP) - /* found a mono speaker with an amp, - must be lfe */ - lfe_pin = pin; - } - } - } - - if (lfe_pin) { - err = create_controls(codec, "LFE", lfe_pin, 1); - if (err < 0) - return err; - } - - return 0; -} - -static int stac9200_parse_auto_config(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - int err; - - if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0) - return err; - - if ((err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0) - return err; - - if ((err = stac9200_auto_create_hp_ctls(codec, &spec->autocfg)) < 0) - return err; - - 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; - } - - err = stac92xx_add_input_source(spec); - if (err < 0) - return err; - - if (spec->autocfg.dig_outs) - spec->multiout.dig_out_nid = 0x05; - if (spec->autocfg.dig_in_pin) - spec->dig_in_nid = 0x04; - - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; - - spec->input_mux = &spec->private_imux; - spec->dinput_mux = &spec->private_dimux; - - return 1; -} - -/* - * Early 2006 Intel Macintoshes with STAC9220X5 codecs seem to have a - * funky external mute control using GPIO pins. - */ - -static void stac_gpio_set(struct hda_codec *codec, unsigned int mask, - unsigned int dir_mask, unsigned int data) -{ - unsigned int gpiostate, gpiomask, gpiodir; - - snd_printdd("%s msk %x dir %x gpio %x\n", __func__, mask, dir_mask, data); - - gpiostate = snd_hda_codec_read(codec, codec->afg, 0, - AC_VERB_GET_GPIO_DATA, 0); - gpiostate = (gpiostate & ~dir_mask) | (data & dir_mask); - - gpiomask = snd_hda_codec_read(codec, codec->afg, 0, - AC_VERB_GET_GPIO_MASK, 0); - gpiomask |= mask; - - gpiodir = snd_hda_codec_read(codec, codec->afg, 0, - AC_VERB_GET_GPIO_DIRECTION, 0); - gpiodir |= dir_mask; - - /* Configure GPIOx as CMOS */ - snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0); - - snd_hda_codec_write(codec, codec->afg, 0, - AC_VERB_SET_GPIO_MASK, gpiomask); - snd_hda_codec_read(codec, codec->afg, 0, - AC_VERB_SET_GPIO_DIRECTION, gpiodir); /* sync */ - - msleep(1); - - snd_hda_codec_read(codec, codec->afg, 0, - AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */ -} - -static int stac_add_event(struct hda_codec *codec, hda_nid_t nid, - unsigned char type, int data) -{ - struct hda_jack_tbl *event; - - event = snd_hda_jack_tbl_new(codec, nid); - if (!event) - return -ENOMEM; - event->action = type; - event->private_data = data; - - return 0; -} - -/* check if given nid is a valid pin and no other events are assigned - * to it. If OK, assign the event, set the unsol flag, and returns 1. - * Otherwise, returns zero. - */ -static int enable_pin_detect(struct hda_codec *codec, hda_nid_t nid, - unsigned int type) -{ - struct hda_jack_tbl *event; - - if (!is_jack_detectable(codec, nid)) - return 0; - event = snd_hda_jack_tbl_new(codec, nid); - if (!event) - return -ENOMEM; - if (event->action && event->action != type) - return 0; - event->action = type; - snd_hda_jack_detect_enable(codec, nid, 0); - return 1; -} - -static int is_nid_out_jack_pin(struct auto_pin_cfg *cfg, hda_nid_t nid) -{ - int i; - for (i = 0; i < cfg->hp_outs; i++) - if (cfg->hp_pins[i] == nid) - return 1; /* nid is a HP-Out */ - for (i = 0; i < cfg->line_outs; i++) - if (cfg->line_out_pins[i] == nid) - return 1; /* nid is a line-Out */ - return 0; /* nid is not a HP-Out */ -}; - -static void stac92xx_power_down(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - - /* power down inactive DACs */ - const hda_nid_t *dac; - for (dac = spec->dac_list; *dac; dac++) - 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 inline int get_int_hint(struct hda_codec *codec, const char *key, - int *valp) -{ - const char *p; - p = snd_hda_get_hint(codec, key); - if (p) { - unsigned long val; - if (!strict_strtoul(p, 0, &val)) { - *valp = val; - return 1; - } - } - return 0; -} - -/* override some hints from the hwdep entry */ -static void stac_store_hints(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - int val; - - val = snd_hda_get_bool_hint(codec, "hp_detect"); - if (val >= 0) - spec->hp_detect = val; - if (get_int_hint(codec, "gpio_mask", &spec->gpio_mask)) { - spec->eapd_mask = spec->gpio_dir = spec->gpio_data = - spec->gpio_mask; - } - if (get_int_hint(codec, "gpio_dir", &spec->gpio_dir)) - spec->gpio_mask &= spec->gpio_mask; - if (get_int_hint(codec, "gpio_data", &spec->gpio_data)) - spec->gpio_dir &= spec->gpio_mask; - if (get_int_hint(codec, "eapd_mask", &spec->eapd_mask)) - spec->eapd_mask &= spec->gpio_mask; - if (get_int_hint(codec, "gpio_mute", &spec->gpio_mute)) - spec->gpio_mute &= spec->gpio_mask; - val = snd_hda_get_bool_hint(codec, "eapd_switch"); - if (val >= 0) - spec->eapd_switch = val; - get_int_hint(codec, "gpio_led_polarity", &spec->gpio_led_polarity); - if (get_int_hint(codec, "gpio_led", &spec->gpio_led)) { - spec->gpio_mask |= spec->gpio_led; - spec->gpio_dir |= spec->gpio_led; - if (spec->gpio_led_polarity) - spec->gpio_data |= spec->gpio_led; - } -} - -static void stac_issue_unsol_events(struct hda_codec *codec, int num_pins, - const hda_nid_t *pins) -{ - while (num_pins--) - stac_issue_unsol_event(codec, *pins++); -} - -/* fake event to set up pins */ -static void stac_fake_hp_events(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - - if (spec->autocfg.hp_outs) - stac_issue_unsol_events(codec, spec->autocfg.hp_outs, - spec->autocfg.hp_pins); - if (spec->autocfg.line_outs && - spec->autocfg.line_out_pins[0] != spec->autocfg.hp_pins[0]) - stac_issue_unsol_events(codec, spec->autocfg.line_outs, - spec->autocfg.line_out_pins); -} - -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); - - /* power down adcs initially */ - if (spec->powerdown_adcs) - for (i = 0; i < spec->num_adcs; i++) - snd_hda_codec_write(codec, - spec->adc_nids[i], 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); - - /* override some hints */ - stac_store_hints(codec); - - /* 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++) { - hda_nid_t nid = cfg->hp_pins[i]; - enable_pin_detect(codec, nid, STAC_HP_EVENT); - } - if (cfg->line_out_type == AUTO_PIN_LINE_OUT && - cfg->speaker_outs > 0) { - /* enable pin-detect for line-outs as well */ - for (i = 0; i < cfg->line_outs; i++) { - hda_nid_t nid = cfg->line_out_pins[i]; - enable_pin_detect(codec, nid, STAC_LO_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); - /* fake event to set up pins */ - stac_fake_hp_events(codec); - } 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); - } - if (spec->auto_mic) { - /* initialize connection to analog input */ - if (spec->dmux_nids) - snd_hda_codec_write_cache(codec, spec->dmux_nids[0], 0, - AC_VERB_SET_CONNECT_SEL, 0); - if (enable_pin_detect(codec, spec->ext_mic.pin, STAC_MIC_EVENT)) - stac_issue_unsol_event(codec, spec->ext_mic.pin); - if (enable_pin_detect(codec, spec->dock_mic.pin, - STAC_MIC_EVENT)) - stac_issue_unsol_event(codec, spec->dock_mic.pin); - } - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - int type = cfg->inputs[i].type; - unsigned int pinctl, conf; - if (type == AUTO_PIN_MIC) { - /* for mic pins, force to initialize */ - pinctl = stac92xx_get_default_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 */ - /* Also, if both INPUT and OUTPUT are set, - * it must be a BIOS bug; need to override, too - */ - if (!(pinctl & AC_PINCTL_IN_EN) || - (pinctl & AC_PINCTL_OUT_EN)) { - pinctl &= ~AC_PINCTL_OUT_EN; - pinctl |= AC_PINCTL_IN_EN; - stac92xx_auto_set_pinctl(codec, nid, pinctl); - } - } - conf = snd_hda_codec_get_pincfg(codec, nid); - if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) { - if (enable_pin_detect(codec, nid, STAC_INSERT_EVENT)) - stac_issue_unsol_event(codec, nid); - } - } - 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_pins[0]) - stac92xx_auto_set_pinctl(codec, cfg->dig_out_pins[0], - 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++) { - hda_nid_t nid = spec->pwr_nids[i]; - unsigned int pinctl, def_conf; - - /* power on when no jack detection is available */ - /* or when the VREF is used for controlling LED */ - if (!spec->hp_detect || - spec->vref_mute_led_nid == nid) { - stac_toggle_power_map(codec, nid, 1); - continue; - } - - if (is_nid_out_jack_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) { - stac_toggle_power_map(codec, nid, 1); - continue; - } - def_conf = snd_hda_codec_get_pincfg(codec, nid); - def_conf = get_defcfg_connect(def_conf); - /* skip any ports that don't have jacks since presence - * detection is useless */ - if (def_conf != AC_JACK_PORT_COMPLEX || - !is_jack_detectable(codec, nid)) { - stac_toggle_power_map(codec, nid, 1); - continue; - } - if (enable_pin_detect(codec, nid, STAC_PWR_EVENT)) { - stac_issue_unsol_event(codec, nid); - continue; - } - /* none of the above, turn the port OFF */ - stac_toggle_power_map(codec, nid, 0); - } - - snd_hda_jack_report_sync(codec); - - /* sync mute LED */ - snd_hda_sync_vmaster_hook(&spec->vmaster_mute); - if (spec->dac_list) - stac92xx_power_down(codec); - return 0; -} - -static void stac92xx_free_kctls(struct hda_codec *codec) -{ - struct sigmatel_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 stac92xx_shutup_pins(struct hda_codec *codec) -{ - unsigned int i, def_conf; - - if (codec->bus->shutdown) - return; - for (i = 0; i < codec->init_pins.used; i++) { - struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); - def_conf = snd_hda_codec_get_pincfg(codec, pin->nid); - if (get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE) - snd_hda_codec_write(codec, pin->nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0); - } -} - -static void stac92xx_shutup(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - - stac92xx_shutup_pins(codec); - - if (spec->eapd_mask) - stac_gpio_set(codec, spec->gpio_mask, - spec->gpio_dir, spec->gpio_data & - ~spec->eapd_mask); -} - -static void stac92xx_free(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - - if (! spec) - return; - - stac92xx_shutup(codec); - - kfree(spec); - snd_hda_detach_beep_device(codec); -} - -static void stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid, - unsigned int flag) -{ - unsigned int old_ctl, pin_ctl; - - pin_ctl = snd_hda_codec_read(codec, nid, - 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00); - - if (pin_ctl & AC_PINCTL_IN_EN) { - /* - * we need to check the current set-up direction of - * shared input pins since they can be switched via - * "xxx as Output" mixer switch - */ - struct sigmatel_spec *spec = codec->spec; - if (nid == spec->line_switch || nid == spec->mic_switch) - return; - } - - old_ctl = pin_ctl; - /* if setting pin direction bits, clear the current - direction bits first */ - if (flag & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN)) - pin_ctl &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); - - pin_ctl |= flag; - if (old_ctl != pin_ctl) - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - pin_ctl); -} - -static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid, - unsigned int flag) -{ - unsigned int pin_ctl = snd_hda_codec_read(codec, nid, - 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00); - if (pin_ctl & flag) - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - pin_ctl & ~flag); -} - -static inline int get_pin_presence(struct hda_codec *codec, hda_nid_t nid) -{ - if (!nid) - return 0; - return snd_hda_jack_detect(codec, nid); -} - -static void stac92xx_line_out_detect(struct hda_codec *codec, - int presence) -{ - struct sigmatel_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - for (i = 0; i < cfg->line_outs; i++) { - if (presence) - break; - presence = get_pin_presence(codec, cfg->line_out_pins[i]); - if (presence) { - unsigned int pinctl; - pinctl = snd_hda_codec_read(codec, - cfg->line_out_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 speakers */ - for (i = 0; i < cfg->speaker_outs; i++) - stac92xx_reset_pinctl(codec, cfg->speaker_pins[i], - AC_PINCTL_OUT_EN); - 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 speakers */ - for (i = 0; i < cfg->speaker_outs; i++) - stac92xx_set_pinctl(codec, cfg->speaker_pins[i], - AC_PINCTL_OUT_EN); - if (spec->eapd_mask && spec->eapd_switch) - stac_gpio_set(codec, spec->gpio_mask, - spec->gpio_dir, spec->gpio_data | - spec->eapd_mask); - } -} - -/* 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 i, presence; - - presence = 0; - if (spec->gpio_mute) - presence = !(snd_hda_codec_read(codec, codec->afg, 0, - AC_VERB_GET_GPIO_DATA, 0) & spec->gpio_mute); - - for (i = 0; i < cfg->hp_outs; i++) { - if (presence) - break; - 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 */ - if (spec->hp_switch) - 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); - } else { - /* enable lineouts */ - if (spec->hp_switch) - 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); - } - stac92xx_line_out_detect(codec, presence); - /* 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 (1 /*presence*/) - stac92xx_set_pinctl(codec, cfg->hp_pins[i], val); -#if 0 /* FIXME */ -/* Resetting the pinctl like below may lead to (a sort of) regressions - * on some devices since they use the HP pin actually for line/speaker - * outs although the default pin config shows a different pin (that is - * wrong and useless). - * - * So, it's basically a problem of default pin configs, likely a BIOS issue. - * But, disabling the code below just works around it, and I'm too tired of - * bug reports with such devices... - */ - else - stac92xx_reset_pinctl(codec, cfg->hp_pins[i], val); -#endif /* FIXME */ - } -} - -static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid, - int enable) -{ - struct sigmatel_spec *spec = codec->spec; - 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; - - idx = 1 << idx; - - 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)); -} - -/* get the pin connection (fixed, none, etc) */ -static unsigned int stac_get_defcfg_connect(struct hda_codec *codec, int idx) -{ - struct sigmatel_spec *spec = codec->spec; - unsigned int cfg; - - cfg = snd_hda_codec_get_pincfg(codec, spec->pin_nids[idx]); - return get_defcfg_connect(cfg); -} - -static int stac92xx_connected_ports(struct hda_codec *codec, - const hda_nid_t *nids, int num_nids) -{ - struct sigmatel_spec *spec = codec->spec; - int idx, num; - unsigned int def_conf; - - for (num = 0; num < num_nids; num++) { - for (idx = 0; idx < spec->num_pins; idx++) - if (spec->pin_nids[idx] == nids[num]) - break; - if (idx >= spec->num_pins) - break; - def_conf = stac_get_defcfg_connect(codec, idx); - if (def_conf == AC_JACK_PORT_NONE) - break; - } - return num; -} - -static void stac92xx_mic_detect(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - struct sigmatel_mic_route *mic; - - if (get_pin_presence(codec, spec->ext_mic.pin)) - mic = &spec->ext_mic; - else if (get_pin_presence(codec, spec->dock_mic.pin)) - mic = &spec->dock_mic; - else - mic = &spec->int_mic; - if (mic->dmux_idx >= 0) - snd_hda_codec_write_cache(codec, spec->dmux_nids[0], 0, - AC_VERB_SET_CONNECT_SEL, - mic->dmux_idx); - if (mic->mux_idx >= 0) - snd_hda_codec_write_cache(codec, spec->mux_nids[0], 0, - AC_VERB_SET_CONNECT_SEL, - mic->mux_idx); -} - -static void handle_unsol_event(struct hda_codec *codec, - struct hda_jack_tbl *event) -{ - struct sigmatel_spec *spec = codec->spec; - int data; - - switch (event->action) { - case STAC_HP_EVENT: - case STAC_LO_EVENT: - stac92xx_hp_detect(codec); - break; - case STAC_MIC_EVENT: - stac92xx_mic_detect(codec); - break; - } - - switch (event->action) { - case STAC_HP_EVENT: - case STAC_LO_EVENT: - case STAC_MIC_EVENT: - case STAC_INSERT_EVENT: - case STAC_PWR_EVENT: - if (spec->num_pwrs > 0) - stac92xx_pin_sense(codec, event->nid); - - switch (codec->subsystem_id) { - case 0x103c308f: - if (event->nid == 0xb) { - int pin = AC_PINCTL_IN_EN; - - if (get_pin_presence(codec, 0xa) - && get_pin_presence(codec, 0xb)) - pin |= AC_PINCTL_VREF_80; - if (!get_pin_presence(codec, 0xb)) - pin |= AC_PINCTL_VREF_80; - - /* toggle VREF state based on mic + hp pin - * status - */ - stac92xx_auto_set_pinctl(codec, 0x0a, pin); - } - } - break; - 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 << event->private_data))); - break; - } -} - -static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid) -{ - struct hda_jack_tbl *event = snd_hda_jack_tbl_get(codec, nid); - if (!event) - return; - handle_unsol_event(codec, event); -} - -static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res) -{ - struct hda_jack_tbl *event; - int tag; - - tag = (res >> 26) & 0x7f; - event = snd_hda_jack_tbl_get_from_tag(codec, tag); - if (!event) - return; - event->jack_dirty = 1; - handle_unsol_event(codec, event); - snd_hda_jack_report_sync(codec); -} - -static int hp_blike_system(u32 subsystem_id); - -static void set_hp_led_gpio(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - unsigned int gpio; - - if (spec->gpio_led) - return; - - gpio = snd_hda_param_read(codec, codec->afg, AC_PAR_GPIO_CAP); - gpio &= AC_GPIO_IO_COUNT; - if (gpio > 3) - spec->gpio_led = 0x08; /* GPIO 3 */ - else - spec->gpio_led = 0x01; /* GPIO 0 */ -} - -/* - * This method searches for the mute LED GPIO configuration - * provided as OEM string in SMBIOS. The format of that string - * is HP_Mute_LED_P_G or HP_Mute_LED_P - * where P can be 0 or 1 and defines mute LED GPIO control state (low/high) - * that corresponds to the NOT muted state of the master volume - * and G is the index of the GPIO to use as the mute LED control (0..9) - * If _G portion is missing it is assigned based on the codec ID - * - * So, HP B-series like systems may have HP_Mute_LED_0 (current models) - * or HP_Mute_LED_0_3 (future models) OEM SMBIOS strings - * - * - * The dv-series laptops don't seem to have the HP_Mute_LED* strings in - * SMBIOS - at least the ones I have seen do not have them - which include - * my own system (HP Pavilion dv6-1110ax) and my cousin's - * HP Pavilion dv9500t CTO. - * Need more information on whether it is true across the entire series. - * -- kunal - */ -static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity) -{ - struct sigmatel_spec *spec = codec->spec; - const struct dmi_device *dev = NULL; - - if ((codec->subsystem_id >> 16) == PCI_VENDOR_ID_HP) { - while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, - NULL, dev))) { - if (sscanf(dev->name, "HP_Mute_LED_%d_%x", - &spec->gpio_led_polarity, - &spec->gpio_led) == 2) { - unsigned int max_gpio; - max_gpio = snd_hda_param_read(codec, codec->afg, - AC_PAR_GPIO_CAP); - max_gpio &= AC_GPIO_IO_COUNT; - if (spec->gpio_led < max_gpio) - spec->gpio_led = 1 << spec->gpio_led; - else - spec->vref_mute_led_nid = spec->gpio_led; - return 1; - } - if (sscanf(dev->name, "HP_Mute_LED_%d", - &spec->gpio_led_polarity) == 1) { - set_hp_led_gpio(codec); - return 1; - } - /* BIOS bug: unfilled OEM string */ - if (strstr(dev->name, "HP_Mute_LED_P_G")) { - set_hp_led_gpio(codec); - switch (codec->subsystem_id) { - case 0x103c148a: - spec->gpio_led_polarity = 0; - break; - default: - spec->gpio_led_polarity = 1; - break; - } - return 1; - } - } - - /* - * Fallback case - if we don't find the DMI strings, - * we statically set the GPIO - if not a B-series system - * and default polarity is provided - */ - if (!hp_blike_system(codec->subsystem_id) && - (default_polarity == 0 || default_polarity == 1)) { - set_hp_led_gpio(codec); - spec->gpio_led_polarity = default_polarity; - return 1; - } - } - return 0; -} - -static int hp_blike_system(u32 subsystem_id) -{ - switch (subsystem_id) { - case 0x103c1520: - case 0x103c1521: - case 0x103c1523: - case 0x103c1524: - case 0x103c1525: - case 0x103c1722: - case 0x103c1723: - case 0x103c1724: - case 0x103c1725: - case 0x103c1726: - case 0x103c1727: - case 0x103c1728: - case 0x103c1729: - case 0x103c172a: - case 0x103c172b: - case 0x103c307e: - case 0x103c307f: - case 0x103c3080: - case 0x103c3081: - case 0x103c7007: - case 0x103c7008: - return 1; - } - return 0; -} - -#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 CONFIG_PM -static int stac92xx_resume(struct hda_codec *codec) -{ - stac92xx_init(codec); - snd_hda_codec_resume_amp(codec); - snd_hda_codec_resume_cache(codec); - /* fake event to set up pins again to override cached values */ - stac_fake_hp_events(codec); - return 0; -} - -static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state) -{ - stac92xx_shutup(codec); - return 0; -} - -static int stac92xx_pre_resume(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - - /* sync mute LED */ - if (spec->vref_mute_led_nid) - stac_vrefout_set(codec, spec->vref_mute_led_nid, - spec->vref_led); - else if (spec->gpio_led) - stac_gpio_set(codec, spec->gpio_mask, - spec->gpio_dir, spec->gpio_data); - return 0; -} - -static void stac92xx_set_power_state(struct hda_codec *codec, hda_nid_t fg, - unsigned int power_state) -{ - unsigned int afg_power_state = power_state; - struct sigmatel_spec *spec = codec->spec; - - if (power_state == AC_PWRST_D3) { - if (spec->vref_mute_led_nid) { - /* with vref-out pin used for mute led control - * codec AFG is prevented from D3 state - */ - afg_power_state = AC_PWRST_D1; - } - /* this delay seems necessary to avoid click noise at power-down */ - msleep(100); - } - snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, - afg_power_state); - snd_hda_codec_set_power_to_all(codec, fg, power_state, true); -} -#else -#define stac92xx_suspend NULL -#define stac92xx_resume NULL -#define stac92xx_pre_resume NULL -#define stac92xx_set_power_state NULL -#endif /* CONFIG_PM */ - -/* update mute-LED accoring to the master switch */ -static void stac92xx_update_led_status(struct hda_codec *codec, int enabled) -{ - struct sigmatel_spec *spec = codec->spec; - int muted = !enabled; - - if (!spec->gpio_led) - return; - - /* LED state is inverted on these systems */ - if (spec->gpio_led_polarity) - muted = !muted; - - if (!spec->vref_mute_led_nid) { - if (muted) - spec->gpio_data |= spec->gpio_led; - else - spec->gpio_data &= ~spec->gpio_led; - stac_gpio_set(codec, spec->gpio_mask, - spec->gpio_dir, spec->gpio_data); - } else { - spec->vref_led = muted ? AC_PINCTL_VREF_50 : AC_PINCTL_VREF_GRD; - stac_vrefout_set(codec, spec->vref_mute_led_nid, - spec->vref_led); - } -} - -static const struct hda_codec_ops stac92xx_patch_ops = { - .build_controls = stac92xx_build_controls, - .build_pcms = stac92xx_build_pcms, - .init = stac92xx_init, - .free = stac92xx_free, - .unsol_event = stac92xx_unsol_event, -#ifdef CONFIG_PM - .suspend = stac92xx_suspend, - .resume = stac92xx_resume, -#endif - .reboot_notify = stac92xx_shutup, -}; - -static int patch_stac9200(struct hda_codec *codec) -{ - struct sigmatel_spec *spec; - int err; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->no_trigger_sense = 1; - codec->spec = spec; - spec->linear_tone_beep = 1; - spec->num_pins = ARRAY_SIZE(stac9200_pin_nids); - spec->pin_nids = stac9200_pin_nids; - spec->board_config = snd_hda_check_board_config(codec, STAC_9200_MODELS, - stac9200_models, - stac9200_cfg_tbl); - if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - else - stac92xx_set_config_regs(codec, - stac9200_brd_tbl[spec->board_config]); - - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = stac9200_dac_nids; - spec->adc_nids = stac9200_adc_nids; - spec->mux_nids = stac9200_mux_nids; - spec->num_muxes = 1; - spec->num_dmics = 0; - spec->num_adcs = 1; - spec->num_pwrs = 0; - - if (spec->board_config == STAC_9200_M4 || - spec->board_config == STAC_9200_M4_2 || - spec->board_config == STAC_9200_OQO) - spec->init = stac9200_eapd_init; - else - spec->init = stac9200_core_init; - spec->mixer = stac9200_mixer; - - if (spec->board_config == STAC_9200_PANASONIC) { - spec->gpio_mask = spec->gpio_dir = 0x09; - spec->gpio_data = 0x00; - } - - err = stac9200_parse_auto_config(codec); - if (err < 0) { - stac92xx_free(codec); - return err; - } - - /* CF-74 has no headphone detection, and the driver should *NOT* - * do detection and HP/speaker toggle because the hardware does it. - */ - if (spec->board_config == STAC_9200_PANASONIC) - spec->hp_detect = 0; - - codec->patch_ops = stac92xx_patch_ops; - - return 0; -} - -static int patch_stac925x(struct hda_codec *codec) -{ - struct sigmatel_spec *spec; - int err; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->no_trigger_sense = 1; - codec->spec = spec; - spec->linear_tone_beep = 1; - spec->num_pins = ARRAY_SIZE(stac925x_pin_nids); - spec->pin_nids = stac925x_pin_nids; - - /* Check first for codec ID */ - spec->board_config = snd_hda_check_board_codec_sid_config(codec, - STAC_925x_MODELS, - stac925x_models, - stac925x_codec_id_cfg_tbl); - - /* Now checks for PCI ID, if codec ID is not found */ - if (spec->board_config < 0) - spec->board_config = snd_hda_check_board_config(codec, - STAC_925x_MODELS, - stac925x_models, - stac925x_cfg_tbl); - again: - if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - else - stac92xx_set_config_regs(codec, - stac925x_brd_tbl[spec->board_config]); - - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = stac925x_dac_nids; - spec->adc_nids = stac925x_adc_nids; - spec->mux_nids = stac925x_mux_nids; - spec->num_muxes = 1; - spec->num_adcs = 1; - spec->num_pwrs = 0; - switch (codec->vendor_id) { - case 0x83847632: /* STAC9202 */ - case 0x83847633: /* STAC9202D */ - case 0x83847636: /* STAC9251 */ - case 0x83847637: /* STAC9251D */ - spec->num_dmics = STAC925X_NUM_DMICS; - spec->dmic_nids = stac925x_dmic_nids; - spec->num_dmuxes = ARRAY_SIZE(stac925x_dmux_nids); - spec->dmux_nids = stac925x_dmux_nids; - break; - default: - spec->num_dmics = 0; - break; - } - - spec->init = stac925x_core_init; - spec->mixer = stac925x_mixer; - spec->num_caps = 1; - spec->capvols = stac925x_capvols; - spec->capsws = stac925x_capsws; - - err = stac92xx_parse_auto_config(codec); - if (!err) { - if (spec->board_config < 0) { - printk(KERN_WARNING "hda_codec: No auto-config is " - "available, default to model=ref\n"); - spec->board_config = STAC_925x_REF; - goto again; - } - err = -EINVAL; - } - if (err < 0) { - stac92xx_free(codec); - return err; - } - - codec->patch_ops = stac92xx_patch_ops; - - return 0; -} - -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) - return -ENOMEM; - - codec->no_trigger_sense = 1; - codec->spec = spec; - spec->linear_tone_beep = 0; - codec->slave_dig_outs = stac92hd73xx_slave_dig_outs; - spec->num_pins = ARRAY_SIZE(stac92hd73xx_pin_nids); - spec->pin_nids = stac92hd73xx_pin_nids; - spec->board_config = snd_hda_check_board_config(codec, - STAC_92HD73XX_MODELS, - stac92hd73xx_models, - stac92hd73xx_cfg_tbl); - /* check codec subsystem id if not found */ - if (spec->board_config < 0) - spec->board_config = - snd_hda_check_board_codec_sid_config(codec, - STAC_92HD73XX_MODELS, stac92hd73xx_models, - stac92hd73xx_codec_id_cfg_tbl); -again: - if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - else - stac92xx_set_config_regs(codec, - stac92hd73xx_brd_tbl[spec->board_config]); - - num_dacs = snd_hda_get_connections(codec, 0x0a, - conn, STAC92HD73_DAC_COUNT + 2) - 1; - - if (num_dacs < 3 || num_dacs > 5) { - printk(KERN_WARNING "hda_codec: Could not determine " - "number of channels defaulting to DAC count\n"); - num_dacs = STAC92HD73_DAC_COUNT; - } - spec->init = stac92hd73xx_core_init; - switch (num_dacs) { - case 0x3: /* 6 Channel */ - spec->aloopback_ctl = stac92hd73xx_6ch_loopback; - break; - case 0x4: /* 8 Channel */ - spec->aloopback_ctl = stac92hd73xx_8ch_loopback; - break; - case 0x5: /* 10 Channel */ - spec->aloopback_ctl = stac92hd73xx_10ch_loopback; - break; - } - spec->multiout.dac_nids = spec->dac_nids; - - spec->aloopback_mask = 0x01; - spec->aloopback_shift = 8; - - spec->digbeep_nid = 0x1c; - spec->mux_nids = stac92hd73xx_mux_nids; - spec->adc_nids = stac92hd73xx_adc_nids; - spec->dmic_nids = stac92hd73xx_dmic_nids; - spec->dmux_nids = stac92hd73xx_dmux_nids; - spec->smux_nids = stac92hd73xx_smux_nids; - - spec->num_muxes = ARRAY_SIZE(stac92hd73xx_mux_nids); - spec->num_adcs = ARRAY_SIZE(stac92hd73xx_adc_nids); - spec->num_dmuxes = ARRAY_SIZE(stac92hd73xx_dmux_nids); - - spec->num_caps = STAC92HD73XX_NUM_CAPS; - spec->capvols = stac92hd73xx_capvols; - spec->capsws = stac92hd73xx_capsws; - - switch (spec->board_config) { - 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->eapd_switch = 0; - - switch (spec->board_config) { - case STAC_DELL_M6_AMIC: /* Analog Mics */ - snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170); - spec->num_dmics = 0; - break; - case STAC_DELL_M6_DMIC: /* Digital Mics */ - snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160); - spec->num_dmics = 1; - break; - case STAC_DELL_M6_BOTH: /* Both */ - snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170); - snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160); - spec->num_dmics = 1; - break; - } - break; - case STAC_ALIENWARE_M17X: - spec->num_dmics = STAC92HD73XX_NUM_DMICS; - spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids); - spec->eapd_switch = 0; - break; - default: - spec->num_dmics = STAC92HD73XX_NUM_DMICS; - spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids); - spec->eapd_switch = 1; - break; - } - if (spec->board_config != STAC_92HD73XX_REF) { - /* GPIO0 High = Enable EAPD */ - spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1; - spec->gpio_data = 0x01; - } - - spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids); - spec->pwr_nids = stac92hd73xx_pwr_nids; - - err = stac92xx_parse_auto_config(codec); - - if (!err) { - if (spec->board_config < 0) { - printk(KERN_WARNING "hda_codec: No auto-config is " - "available, default to model=ref\n"); - spec->board_config = STAC_92HD73XX_REF; - goto again; - } - err = -EINVAL; - } - - if (err < 0) { - stac92xx_free(codec); - 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; -} - -static int hp_bnb2011_with_dock(struct hda_codec *codec) -{ - if (codec->vendor_id != 0x111d7605 && - codec->vendor_id != 0x111d76d1) - return 0; - - switch (codec->subsystem_id) { - case 0x103c1618: - case 0x103c1619: - case 0x103c161a: - case 0x103c161b: - case 0x103c161c: - case 0x103c161d: - case 0x103c161e: - case 0x103c161f: - - case 0x103c162a: - case 0x103c162b: - - case 0x103c1630: - case 0x103c1631: - - case 0x103c1633: - case 0x103c1634: - case 0x103c1635: - - case 0x103c3587: - case 0x103c3588: - case 0x103c3589: - case 0x103c358a: - - case 0x103c3667: - case 0x103c3668: - case 0x103c3669: - - return 1; - } - return 0; -} - -static void stac92hd8x_add_pin(struct hda_codec *codec, hda_nid_t nid) -{ - struct sigmatel_spec *spec = codec->spec; - unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); - int i; - - spec->auto_pin_nids[spec->auto_pin_cnt] = nid; - spec->auto_pin_cnt++; - - if (get_defcfg_device(def_conf) == AC_JACK_MIC_IN && - get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE) { - for (i = 0; i < ARRAY_SIZE(stac92hd83xxx_dmic_nids); i++) { - if (nid == stac92hd83xxx_dmic_nids[i]) { - spec->auto_dmic_nids[spec->auto_dmic_cnt] = nid; - spec->auto_dmic_cnt++; - } - } - } -} - -static void stac92hd8x_add_adc(struct hda_codec *codec, hda_nid_t nid) -{ - struct sigmatel_spec *spec = codec->spec; - - spec->auto_adc_nids[spec->auto_adc_cnt] = nid; - spec->auto_adc_cnt++; -} - -static void stac92hd8x_add_mux(struct hda_codec *codec, hda_nid_t nid) -{ - int i, j; - struct sigmatel_spec *spec = codec->spec; - - for (i = 0; i < spec->auto_adc_cnt; i++) { - if (get_connection_index(codec, - spec->auto_adc_nids[i], nid) >= 0) { - /* mux and volume for adc_nids[i] */ - if (!spec->auto_mux_nids[i]) { - spec->auto_mux_nids[i] = nid; - /* 92hd codecs capture volume is in mux */ - spec->auto_capvols[i] = HDA_COMPOSE_AMP_VAL(nid, - 3, 0, HDA_OUTPUT); - } - for (j = 0; j < spec->auto_dmic_cnt; j++) { - if (get_connection_index(codec, nid, - spec->auto_dmic_nids[j]) >= 0) { - /* dmux for adc_nids[i] */ - if (!spec->auto_dmux_nids[i]) - spec->auto_dmux_nids[i] = nid; - break; - } - } - break; - } - } -} - -static void stac92hd8x_fill_auto_spec(struct hda_codec *codec) -{ - hda_nid_t nid, end_nid; - unsigned int wid_caps, wid_type; - struct sigmatel_spec *spec = codec->spec; - - end_nid = codec->start_nid + codec->num_nodes; - - for (nid = codec->start_nid; nid < end_nid; nid++) { - wid_caps = get_wcaps(codec, nid); - wid_type = get_wcaps_type(wid_caps); - - if (wid_type == AC_WID_PIN) - stac92hd8x_add_pin(codec, nid); - - if (wid_type == AC_WID_AUD_IN && !(wid_caps & AC_WCAP_DIGITAL)) - stac92hd8x_add_adc(codec, nid); - } - - for (nid = codec->start_nid; nid < end_nid; nid++) { - wid_caps = get_wcaps(codec, nid); - wid_type = get_wcaps_type(wid_caps); - - if (wid_type == AC_WID_AUD_SEL) - stac92hd8x_add_mux(codec, nid); - } - - spec->pin_nids = spec->auto_pin_nids; - spec->num_pins = spec->auto_pin_cnt; - spec->adc_nids = spec->auto_adc_nids; - spec->num_adcs = spec->auto_adc_cnt; - spec->capvols = spec->auto_capvols; - spec->capsws = spec->auto_capvols; - spec->num_caps = spec->auto_adc_cnt; - spec->mux_nids = spec->auto_mux_nids; - spec->num_muxes = spec->auto_adc_cnt; - spec->dmux_nids = spec->auto_dmux_nids; - spec->num_dmuxes = spec->auto_adc_cnt; - spec->dmic_nids = spec->auto_dmic_nids; - spec->num_dmics = spec->auto_dmic_cnt; -} - -static int patch_stac92hd83xxx(struct hda_codec *codec) -{ - struct sigmatel_spec *spec; - int err; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - if (hp_bnb2011_with_dock(codec)) { - snd_hda_codec_set_pincfg(codec, 0xa, 0x2101201f); - snd_hda_codec_set_pincfg(codec, 0xf, 0x2181205e); - } - - codec->no_trigger_sense = 1; - codec->spec = spec; - - stac92hd8x_fill_auto_spec(codec); - - spec->linear_tone_beep = 0; - codec->slave_dig_outs = stac92hd83xxx_slave_dig_outs; - spec->digbeep_nid = 0x21; - spec->pwr_nids = stac92hd83xxx_pwr_nids; - spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids); - spec->multiout.dac_nids = spec->dac_nids; - spec->init = stac92hd83xxx_core_init; - - spec->board_config = snd_hda_check_board_config(codec, - STAC_92HD83XXX_MODELS, - stac92hd83xxx_models, - stac92hd83xxx_cfg_tbl); - /* check codec subsystem id if not found */ - if (spec->board_config < 0) - spec->board_config = - snd_hda_check_board_codec_sid_config(codec, - STAC_92HD83XXX_MODELS, stac92hd83xxx_models, - stac92hd83xxx_codec_id_cfg_tbl); -again: - if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - else - stac92xx_set_config_regs(codec, - stac92hd83xxx_brd_tbl[spec->board_config]); - - codec->patch_ops = stac92xx_patch_ops; - - switch (spec->board_config) { - case STAC_HP_ZEPHYR: - spec->init = stac92hd83xxx_hp_zephyr_init; - break; - } - - if (find_mute_led_cfg(codec, -1/*no default cfg*/)) - snd_printd("mute LED gpio %d polarity %d\n", - spec->gpio_led, - spec->gpio_led_polarity); - - if (spec->gpio_led) { - if (!spec->vref_mute_led_nid) { - spec->gpio_mask |= spec->gpio_led; - spec->gpio_dir |= spec->gpio_led; - spec->gpio_data |= spec->gpio_led; - } else { - codec->patch_ops.set_power_state = - stac92xx_set_power_state; - } -#ifdef CONFIG_PM - codec->patch_ops.pre_resume = stac92xx_pre_resume; -#endif - } - - err = stac92xx_parse_auto_config(codec); - if (!err) { - if (spec->board_config < 0) { - printk(KERN_WARNING "hda_codec: No auto-config is " - "available, default to model=ref\n"); - spec->board_config = STAC_92HD83XXX_REF; - goto again; - } - err = -EINVAL; - } - - if (err < 0) { - stac92xx_free(codec); - return err; - } - - codec->proc_widget_hook = stac92hd_proc_hook; - - return 0; -} - -static int stac92hd71bxx_connected_smuxes(struct hda_codec *codec, - hda_nid_t dig0pin) -{ - struct sigmatel_spec *spec = codec->spec; - int idx; - - for (idx = 0; idx < spec->num_pins; idx++) - if (spec->pin_nids[idx] == dig0pin) - break; - if ((idx + 2) >= spec->num_pins) - return 0; - - /* dig1pin case */ - if (stac_get_defcfg_connect(codec, idx + 1) != AC_JACK_PORT_NONE) - return 2; - - /* dig0pin + dig2pin case */ - if (stac_get_defcfg_connect(codec, idx + 2) != AC_JACK_PORT_NONE) - return 2; - if (stac_get_defcfg_connect(codec, idx) != AC_JACK_PORT_NONE) - return 1; - else - return 0; -} - -/* HP dv7 bass switch - GPIO5 */ -#define stac_hp_bass_gpio_info snd_ctl_boolean_mono_info -static int stac_hp_bass_gpio_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - ucontrol->value.integer.value[0] = !!(spec->gpio_data & 0x20); - return 0; -} - -static int stac_hp_bass_gpio_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - unsigned int gpio_data; - - gpio_data = (spec->gpio_data & ~0x20) | - (ucontrol->value.integer.value[0] ? 0x20 : 0); - if (gpio_data == spec->gpio_data) - return 0; - spec->gpio_data = gpio_data; - stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data); - return 1; -} - -static const struct snd_kcontrol_new stac_hp_bass_sw_ctrl = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .info = stac_hp_bass_gpio_info, - .get = stac_hp_bass_gpio_get, - .put = stac_hp_bass_gpio_put, -}; - -static int stac_add_hp_bass_switch(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - - if (!stac_control_new(spec, &stac_hp_bass_sw_ctrl, - "Bass Speaker Playback Switch", 0)) - return -ENOMEM; - - spec->gpio_mask |= 0x20; - spec->gpio_dir |= 0x20; - spec->gpio_data |= 0x20; - return 0; -} - -static int patch_stac92hd71bxx(struct hda_codec *codec) -{ - struct sigmatel_spec *spec; - const struct hda_verb *unmute_init = stac92hd71bxx_unmute_core_init; - unsigned int pin_cfg; - int err = 0; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->no_trigger_sense = 1; - codec->spec = spec; - spec->linear_tone_beep = 0; - codec->patch_ops = stac92xx_patch_ops; - spec->num_pins = STAC92HD71BXX_NUM_PINS; - switch (codec->vendor_id) { - case 0x111d76b6: - case 0x111d76b7: - spec->pin_nids = stac92hd71bxx_pin_nids_4port; - break; - case 0x111d7603: - case 0x111d7608: - /* On 92HD75Bx 0x27 isn't a pin nid */ - spec->num_pins--; - /* fallthrough */ - default: - spec->pin_nids = stac92hd71bxx_pin_nids_6port; - } - spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids); - spec->board_config = snd_hda_check_board_config(codec, - STAC_92HD71BXX_MODELS, - stac92hd71bxx_models, - stac92hd71bxx_cfg_tbl); -again: - if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - else - stac92xx_set_config_regs(codec, - stac92hd71bxx_brd_tbl[spec->board_config]); - - if (spec->board_config != STAC_92HD71BXX_REF) { - /* GPIO0 = EAPD */ - spec->gpio_mask = 0x01; - spec->gpio_dir = 0x01; - spec->gpio_data = 0x01; - } - - spec->dmic_nids = stac92hd71bxx_dmic_nids; - spec->dmux_nids = stac92hd71bxx_dmux_nids; - - spec->num_caps = STAC92HD71BXX_NUM_CAPS; - spec->capvols = stac92hd71bxx_capvols; - spec->capsws = stac92hd71bxx_capsws; - - switch (codec->vendor_id) { - case 0x111d76b6: /* 4 Port without Analog Mixer */ - case 0x111d76b7: - unmute_init++; - /* fallthru */ - case 0x111d76b4: /* 6 Port without Analog Mixer */ - case 0x111d76b5: - spec->init = stac92hd71bxx_core_init; - codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs; - spec->num_dmics = stac92xx_connected_ports(codec, - stac92hd71bxx_dmic_nids, - STAC92HD71BXX_NUM_DMICS); - break; - case 0x111d7608: /* 5 Port with Analog Mixer */ - switch (spec->board_config) { - case STAC_HP_M4: - /* Enable VREF power saving on GPIO1 detect */ - err = stac_add_event(codec, 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_jack_detect_enable(codec, codec->afg, 0); - spec->gpio_mask |= 0x02; - break; - } - if ((codec->revision_id & 0xf) == 0 || - (codec->revision_id & 0xf) == 1) - spec->stream_delay = 40; /* 40 milliseconds */ - - /* disable VSW */ - spec->init = stac92hd71bxx_core_init; - unmute_init++; - snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0); - snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3); - spec->dmic_nids = stac92hd71bxx_dmic_5port_nids; - spec->num_dmics = stac92xx_connected_ports(codec, - stac92hd71bxx_dmic_5port_nids, - STAC92HD71BXX_NUM_DMICS - 1); - break; - case 0x111d7603: /* 6 Port with Analog Mixer */ - if ((codec->revision_id & 0xf) == 1) - spec->stream_delay = 40; /* 40 milliseconds */ - - /* fallthru */ - default: - spec->init = stac92hd71bxx_core_init; - codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs; - spec->num_dmics = stac92xx_connected_ports(codec, - stac92hd71bxx_dmic_nids, - STAC92HD71BXX_NUM_DMICS); - break; - } - - if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP) - snd_hda_sequence_write_cache(codec, unmute_init); - - spec->aloopback_ctl = stac92hd71bxx_loopback; - spec->aloopback_mask = 0x50; - spec->aloopback_shift = 0; - - spec->powerdown_adcs = 1; - spec->digbeep_nid = 0x26; - spec->mux_nids = stac92hd71bxx_mux_nids; - spec->adc_nids = stac92hd71bxx_adc_nids; - spec->smux_nids = stac92hd71bxx_smux_nids; - spec->pwr_nids = stac92hd71bxx_pwr_nids; - - spec->num_muxes = ARRAY_SIZE(stac92hd71bxx_mux_nids); - spec->num_adcs = ARRAY_SIZE(stac92hd71bxx_adc_nids); - spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids); - spec->num_smuxes = stac92hd71bxx_connected_smuxes(codec, 0x1e); - - snd_printdd("Found board config: %d\n", spec->board_config); - - switch (spec->board_config) { - case STAC_HP_M4: - /* enable internal microphone */ - snd_hda_codec_set_pincfg(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 = 1; - break; - case STAC_HP_DV4_1222NR: - spec->num_dmics = 1; - /* I don't know if it needs 1 or 2 smuxes - will wait for - * bug reports to fix if needed - */ - spec->num_smuxes = 1; - spec->num_dmuxes = 1; - /* fallthrough */ - case STAC_HP_DV4: - spec->gpio_led = 0x01; - /* fallthrough */ - case STAC_HP_DV5: - snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010); - stac92xx_auto_set_pinctl(codec, 0x0d, AC_PINCTL_OUT_EN); - /* HP dv6 gives the headphone pin as a line-out. Thus we - * need to set hp_detect flag here to force to enable HP - * detection. - */ - spec->hp_detect = 1; - break; - case STAC_HP_HDX: - spec->num_dmics = 1; - spec->num_dmuxes = 1; - spec->num_smuxes = 1; - spec->gpio_led = 0x08; - break; - } - - if (hp_blike_system(codec->subsystem_id)) { - pin_cfg = snd_hda_codec_get_pincfg(codec, 0x0f); - if (get_defcfg_device(pin_cfg) == AC_JACK_LINE_OUT || - get_defcfg_device(pin_cfg) == AC_JACK_SPEAKER || - get_defcfg_device(pin_cfg) == AC_JACK_HP_OUT) { - /* It was changed in the BIOS to just satisfy MS DTM. - * Lets turn it back into slaved HP - */ - pin_cfg = (pin_cfg & (~AC_DEFCFG_DEVICE)) - | (AC_JACK_HP_OUT << - AC_DEFCFG_DEVICE_SHIFT); - pin_cfg = (pin_cfg & (~(AC_DEFCFG_DEF_ASSOC - | AC_DEFCFG_SEQUENCE))) - | 0x1f; - snd_hda_codec_set_pincfg(codec, 0x0f, pin_cfg); - } - } - - if (find_mute_led_cfg(codec, 1)) - snd_printd("mute LED gpio %d polarity %d\n", - spec->gpio_led, - spec->gpio_led_polarity); - - if (spec->gpio_led) { - if (!spec->vref_mute_led_nid) { - spec->gpio_mask |= spec->gpio_led; - spec->gpio_dir |= spec->gpio_led; - spec->gpio_data |= spec->gpio_led; - } else { - codec->patch_ops.set_power_state = - stac92xx_set_power_state; - } -#ifdef CONFIG_PM - codec->patch_ops.pre_resume = stac92xx_pre_resume; -#endif - } - - spec->multiout.dac_nids = spec->dac_nids; - - err = stac92xx_parse_auto_config(codec); - if (!err) { - if (spec->board_config < 0) { - printk(KERN_WARNING "hda_codec: No auto-config is " - "available, default to model=ref\n"); - spec->board_config = STAC_92HD71BXX_REF; - goto again; - } - err = -EINVAL; - } - - if (err < 0) { - stac92xx_free(codec); - return err; - } - - /* enable bass on HP dv7 */ - if (spec->board_config == STAC_HP_DV4 || - spec->board_config == STAC_HP_DV5) { - unsigned int cap; - cap = snd_hda_param_read(codec, 0x1, AC_PAR_GPIO_CAP); - cap &= AC_GPIO_IO_COUNT; - if (cap >= 6) - stac_add_hp_bass_switch(codec); - } - - codec->proc_widget_hook = stac92hd7x_proc_hook; - - return 0; -} - -static int patch_stac922x(struct hda_codec *codec) -{ - struct sigmatel_spec *spec; - int err; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->no_trigger_sense = 1; - codec->spec = spec; - spec->linear_tone_beep = 1; - spec->num_pins = ARRAY_SIZE(stac922x_pin_nids); - spec->pin_nids = stac922x_pin_nids; - spec->board_config = snd_hda_check_board_config(codec, STAC_922X_MODELS, - stac922x_models, - stac922x_cfg_tbl); - if (spec->board_config == STAC_INTEL_MAC_AUTO) { - spec->gpio_mask = spec->gpio_dir = 0x03; - spec->gpio_data = 0x03; - /* Intel Macs have all same PCI SSID, so we need to check - * codec SSID to distinguish the exact models - */ - printk(KERN_INFO "hda_codec: STAC922x, Apple subsys_id=%x\n", codec->subsystem_id); - switch (codec->subsystem_id) { - - case 0x106b0800: - spec->board_config = STAC_INTEL_MAC_V1; - break; - case 0x106b0600: - case 0x106b0700: - spec->board_config = STAC_INTEL_MAC_V2; - break; - case 0x106b0e00: - case 0x106b0f00: - case 0x106b1600: - case 0x106b1700: - case 0x106b0200: - case 0x106b1e00: - spec->board_config = STAC_INTEL_MAC_V3; - break; - case 0x106b1a00: - case 0x00000100: - spec->board_config = STAC_INTEL_MAC_V4; - break; - case 0x106b0a00: - case 0x106b2200: - spec->board_config = STAC_INTEL_MAC_V5; - break; - default: - spec->board_config = STAC_INTEL_MAC_V3; - break; - } - } - - again: - if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - else - stac92xx_set_config_regs(codec, - stac922x_brd_tbl[spec->board_config]); - - spec->adc_nids = stac922x_adc_nids; - spec->mux_nids = stac922x_mux_nids; - spec->num_muxes = ARRAY_SIZE(stac922x_mux_nids); - spec->num_adcs = ARRAY_SIZE(stac922x_adc_nids); - spec->num_dmics = 0; - spec->num_pwrs = 0; - - spec->init = stac922x_core_init; - - spec->num_caps = STAC922X_NUM_CAPS; - spec->capvols = stac922x_capvols; - spec->capsws = stac922x_capsws; - - spec->multiout.dac_nids = spec->dac_nids; - - err = stac92xx_parse_auto_config(codec); - if (!err) { - if (spec->board_config < 0) { - printk(KERN_WARNING "hda_codec: No auto-config is " - "available, default to model=ref\n"); - spec->board_config = STAC_D945_REF; - goto again; - } - err = -EINVAL; - } - if (err < 0) { - stac92xx_free(codec); - return err; - } - - codec->patch_ops = stac92xx_patch_ops; - - /* Fix Mux capture level; max to 2 */ - snd_hda_override_amp_caps(codec, 0x12, HDA_OUTPUT, - (0 << AC_AMPCAP_OFFSET_SHIFT) | - (2 << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (0 << AC_AMPCAP_MUTE_SHIFT)); - - return 0; -} - -static int patch_stac927x(struct hda_codec *codec) -{ - struct sigmatel_spec *spec; - int err; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->no_trigger_sense = 1; - codec->spec = spec; - spec->linear_tone_beep = 1; - codec->slave_dig_outs = stac927x_slave_dig_outs; - spec->num_pins = ARRAY_SIZE(stac927x_pin_nids); - spec->pin_nids = stac927x_pin_nids; - spec->board_config = snd_hda_check_board_config(codec, STAC_927X_MODELS, - stac927x_models, - stac927x_cfg_tbl); - again: - if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - else - stac92xx_set_config_regs(codec, - stac927x_brd_tbl[spec->board_config]); - - spec->digbeep_nid = 0x23; - spec->adc_nids = stac927x_adc_nids; - spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids); - spec->mux_nids = stac927x_mux_nids; - spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids); - spec->smux_nids = stac927x_smux_nids; - spec->num_smuxes = ARRAY_SIZE(stac927x_smux_nids); - spec->spdif_labels = stac927x_spdif_labels; - spec->dac_list = stac927x_dac_nids; - spec->multiout.dac_nids = spec->dac_nids; - - if (spec->board_config != STAC_D965_REF) { - /* GPIO0 High = Enable EAPD */ - spec->eapd_mask = spec->gpio_mask = 0x01; - spec->gpio_dir = spec->gpio_data = 0x01; - } - - switch (spec->board_config) { - case STAC_D965_3ST: - case STAC_D965_5ST: - /* GPIO0 High = Enable EAPD */ - spec->num_dmics = 0; - spec->init = d965_core_init; - break; - case STAC_DELL_BIOS: - switch (codec->subsystem_id) { - case 0x10280209: - case 0x1028022e: - /* correct the device field to SPDIF out */ - snd_hda_codec_set_pincfg(codec, 0x21, 0x01442070); - break; - } - /* configure the analog microphone on some laptops */ - snd_hda_codec_set_pincfg(codec, 0x0c, 0x90a79130); - /* correct the front output jack as a hp out */ - snd_hda_codec_set_pincfg(codec, 0x0f, 0x0227011f); - /* correct the front input jack as a mic */ - snd_hda_codec_set_pincfg(codec, 0x0e, 0x02a79130); - /* fallthru */ - case STAC_DELL_3ST: - if (codec->subsystem_id != 0x1028022f) { - /* GPIO2 High = Enable EAPD */ - spec->eapd_mask = spec->gpio_mask = 0x04; - spec->gpio_dir = spec->gpio_data = 0x04; - } - spec->dmic_nids = stac927x_dmic_nids; - spec->num_dmics = STAC927X_NUM_DMICS; - - spec->init = dell_3st_core_init; - spec->dmux_nids = stac927x_dmux_nids; - spec->num_dmuxes = ARRAY_SIZE(stac927x_dmux_nids); - break; - case STAC_927X_VOLKNOB: - spec->num_dmics = 0; - spec->init = stac927x_volknob_core_init; - break; - default: - spec->num_dmics = 0; - spec->init = stac927x_core_init; - break; - } - - spec->num_caps = STAC927X_NUM_CAPS; - spec->capvols = stac927x_capvols; - spec->capsws = stac927x_capsws; - - spec->num_pwrs = 0; - spec->aloopback_ctl = stac927x_loopback; - spec->aloopback_mask = 0x40; - spec->aloopback_shift = 0; - spec->eapd_switch = 1; - - err = stac92xx_parse_auto_config(codec); - if (!err) { - if (spec->board_config < 0) { - printk(KERN_WARNING "hda_codec: No auto-config is " - "available, default to model=ref\n"); - spec->board_config = STAC_D965_REF; - goto again; - } - err = -EINVAL; - } - if (err < 0) { - stac92xx_free(codec); - return err; - } - - codec->patch_ops = stac92xx_patch_ops; - - codec->proc_widget_hook = stac927x_proc_hook; - - /* - * !!FIXME!! - * The STAC927x seem to require fairly long delays for certain - * command sequences. With too short delays (even if the answer - * is set to RIRB properly), it results in the silence output - * on some hardwares like Dell. - * - * The below flag enables the longer delay (see get_response - * in hda_intel.c). - */ - 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; -} - -static int patch_stac9205(struct hda_codec *codec) -{ - struct sigmatel_spec *spec; - int err; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->no_trigger_sense = 1; - codec->spec = spec; - spec->linear_tone_beep = 1; - spec->num_pins = ARRAY_SIZE(stac9205_pin_nids); - spec->pin_nids = stac9205_pin_nids; - spec->board_config = snd_hda_check_board_config(codec, STAC_9205_MODELS, - stac9205_models, - stac9205_cfg_tbl); - again: - if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - else - stac92xx_set_config_regs(codec, - stac9205_brd_tbl[spec->board_config]); - - spec->digbeep_nid = 0x23; - spec->adc_nids = stac9205_adc_nids; - spec->num_adcs = ARRAY_SIZE(stac9205_adc_nids); - spec->mux_nids = stac9205_mux_nids; - spec->num_muxes = ARRAY_SIZE(stac9205_mux_nids); - spec->smux_nids = stac9205_smux_nids; - spec->num_smuxes = ARRAY_SIZE(stac9205_smux_nids); - spec->dmic_nids = stac9205_dmic_nids; - spec->num_dmics = STAC9205_NUM_DMICS; - spec->dmux_nids = stac9205_dmux_nids; - spec->num_dmuxes = ARRAY_SIZE(stac9205_dmux_nids); - spec->num_pwrs = 0; - - spec->init = stac9205_core_init; - spec->aloopback_ctl = stac9205_loopback; - - spec->num_caps = STAC9205_NUM_CAPS; - spec->capvols = stac9205_capvols; - spec->capsws = stac9205_capsws; - - spec->aloopback_mask = 0x40; - spec->aloopback_shift = 0; - /* Turn on/off EAPD per HP plugging */ - if (spec->board_config != STAC_9205_EAPD) - spec->eapd_switch = 1; - spec->multiout.dac_nids = spec->dac_nids; - - switch (spec->board_config){ - case STAC_9205_DELL_M43: - /* Enable SPDIF in/out */ - snd_hda_codec_set_pincfg(codec, 0x1f, 0x01441030); - snd_hda_codec_set_pincfg(codec, 0x20, 0x1c410030); - - /* Enable unsol response for GPIO4/Dock HP connection */ - err = stac_add_event(codec, 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_jack_detect_enable(codec, codec->afg, 0); - - spec->gpio_dir = 0x0b; - spec->eapd_mask = 0x01; - spec->gpio_mask = 0x1b; - spec->gpio_mute = 0x10; - /* GPIO0 High = EAPD, GPIO1 Low = Headphone Mute, - * GPIO3 Low = DRM - */ - spec->gpio_data = 0x01; - break; - case STAC_9205_REF: - /* SPDIF-In enabled */ - break; - default: - /* GPIO0 High = EAPD */ - spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1; - spec->gpio_data = 0x01; - break; - } - - err = stac92xx_parse_auto_config(codec); - if (!err) { - if (spec->board_config < 0) { - printk(KERN_WARNING "hda_codec: No auto-config is " - "available, default to model=ref\n"); - spec->board_config = STAC_9205_REF; - goto again; - } - err = -EINVAL; - } - if (err < 0) { - stac92xx_free(codec); - return err; - } - - codec->patch_ops = stac92xx_patch_ops; - - codec->proc_widget_hook = stac9205_proc_hook; - - return 0; -} - -/* - * STAC9872 hack - */ - -static const struct hda_verb stac9872_core_init[] = { - {0x15, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mic-sel: 0a,0d,14,02 */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Mic-in -> 0x9 */ - {} -}; - -static const hda_nid_t stac9872_pin_nids[] = { - 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x11, 0x13, 0x14, -}; - -static const hda_nid_t stac9872_adc_nids[] = { - 0x8 /*,0x6*/ -}; - -static const hda_nid_t stac9872_mux_nids[] = { - 0x15 -}; - -static const unsigned long stac9872_capvols[] = { - HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT), -}; -#define stac9872_capsws stac9872_capvols - -static const unsigned int stac9872_vaio_pin_configs[9] = { - 0x03211020, 0x411111f0, 0x411111f0, 0x03a15030, - 0x411111f0, 0x90170110, 0x411111f0, 0x411111f0, - 0x90a7013e -}; - -static const char * const stac9872_models[STAC_9872_MODELS] = { - [STAC_9872_AUTO] = "auto", - [STAC_9872_VAIO] = "vaio", -}; - -static const unsigned int *stac9872_brd_tbl[STAC_9872_MODELS] = { - [STAC_9872_VAIO] = stac9872_vaio_pin_configs, -}; - -static const struct snd_pci_quirk stac9872_cfg_tbl[] = { - SND_PCI_QUIRK_MASK(0x104d, 0xfff0, 0x81e0, - "Sony VAIO F/S", STAC_9872_VAIO), - {} /* terminator */ -}; - -static int patch_stac9872(struct hda_codec *codec) -{ - struct sigmatel_spec *spec; - int err; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - codec->no_trigger_sense = 1; - codec->spec = spec; - spec->linear_tone_beep = 1; - spec->num_pins = ARRAY_SIZE(stac9872_pin_nids); - spec->pin_nids = stac9872_pin_nids; - - spec->board_config = snd_hda_check_board_config(codec, STAC_9872_MODELS, - stac9872_models, - stac9872_cfg_tbl); - if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - else - stac92xx_set_config_regs(codec, - stac9872_brd_tbl[spec->board_config]); - - spec->multiout.dac_nids = spec->dac_nids; - spec->num_adcs = ARRAY_SIZE(stac9872_adc_nids); - spec->adc_nids = stac9872_adc_nids; - spec->num_muxes = ARRAY_SIZE(stac9872_mux_nids); - spec->mux_nids = stac9872_mux_nids; - spec->init = stac9872_core_init; - spec->num_caps = 1; - spec->capvols = stac9872_capvols; - spec->capsws = stac9872_capsws; - - err = stac92xx_parse_auto_config(codec); - if (err < 0) { - stac92xx_free(codec); - return -EINVAL; - } - spec->input_mux = &spec->private_imux; - codec->patch_ops = stac92xx_patch_ops; - return 0; -} - - -/* - * patch entries - */ -static const 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 }, - { .id = 0x83847880, .name = "STAC9220 A2", .patch = patch_stac922x }, - { .id = 0x83847681, .name = "STAC9220D/9223D A2", .patch = patch_stac922x }, - { .id = 0x83847682, .name = "STAC9221 A2", .patch = patch_stac922x }, - { .id = 0x83847683, .name = "STAC9221D A2", .patch = patch_stac922x }, - { .id = 0x83847618, .name = "STAC9227", .patch = patch_stac927x }, - { .id = 0x83847619, .name = "STAC9227", .patch = patch_stac927x }, - { .id = 0x83847616, .name = "STAC9228", .patch = patch_stac927x }, - { .id = 0x83847617, .name = "STAC9228", .patch = patch_stac927x }, - { .id = 0x83847614, .name = "STAC9229", .patch = patch_stac927x }, - { .id = 0x83847615, .name = "STAC9229", .patch = patch_stac927x }, - { .id = 0x83847620, .name = "STAC9274", .patch = patch_stac927x }, - { .id = 0x83847621, .name = "STAC9274D", .patch = patch_stac927x }, - { .id = 0x83847622, .name = "STAC9273X", .patch = patch_stac927x }, - { .id = 0x83847623, .name = "STAC9273D", .patch = patch_stac927x }, - { .id = 0x83847624, .name = "STAC9272X", .patch = patch_stac927x }, - { .id = 0x83847625, .name = "STAC9272D", .patch = patch_stac927x }, - { .id = 0x83847626, .name = "STAC9271X", .patch = patch_stac927x }, - { .id = 0x83847627, .name = "STAC9271D", .patch = patch_stac927x }, - { .id = 0x83847628, .name = "STAC9274X5NH", .patch = patch_stac927x }, - { .id = 0x83847629, .name = "STAC9274D5NH", .patch = patch_stac927x }, - { .id = 0x83847632, .name = "STAC9202", .patch = patch_stac925x }, - { .id = 0x83847633, .name = "STAC9202D", .patch = patch_stac925x }, - { .id = 0x83847634, .name = "STAC9250", .patch = patch_stac925x }, - { .id = 0x83847635, .name = "STAC9250D", .patch = patch_stac925x }, - { .id = 0x83847636, .name = "STAC9251", .patch = patch_stac925x }, - { .id = 0x83847637, .name = "STAC9250D", .patch = patch_stac925x }, - { .id = 0x83847645, .name = "92HD206X", .patch = patch_stac927x }, - { .id = 0x83847646, .name = "92HD206D", .patch = patch_stac927x }, - /* The following does not take into account .id=0x83847661 when subsys = - * 104D0C00 which is STAC9225s. Because of this, some SZ Notebooks are - * currently not fully supported. - */ - { .id = 0x83847661, .name = "CXD9872RD/K", .patch = patch_stac9872 }, - { .id = 0x83847662, .name = "STAC9872AK", .patch = patch_stac9872 }, - { .id = 0x83847664, .name = "CXD9872AKD", .patch = patch_stac9872 }, - { .id = 0x83847698, .name = "STAC9205", .patch = patch_stac9205 }, - { .id = 0x838476a0, .name = "STAC9205", .patch = patch_stac9205 }, - { .id = 0x838476a1, .name = "STAC9205D", .patch = patch_stac9205 }, - { .id = 0x838476a2, .name = "STAC9204", .patch = patch_stac9205 }, - { .id = 0x838476a3, .name = "STAC9204D", .patch = patch_stac9205 }, - { .id = 0x838476a4, .name = "STAC9255", .patch = patch_stac9205 }, - { .id = 0x838476a5, .name = "STAC9255D", .patch = patch_stac9205 }, - { .id = 0x838476a6, .name = "STAC9254", .patch = patch_stac9205 }, - { .id = 0x838476a7, .name = "STAC9254D", .patch = patch_stac9205 }, - { .id = 0x111d7603, .name = "92HD75B3X5", .patch = patch_stac92hd71bxx}, - { .id = 0x111d7604, .name = "92HD83C1X5", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76d4, .name = "92HD83C1C5", .patch = patch_stac92hd83xxx}, - { .id = 0x111d7605, .name = "92HD81B1X5", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76d5, .name = "92HD81B1C5", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76d1, .name = "92HD87B1/3", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76d9, .name = "92HD87B2/4", .patch = patch_stac92hd83xxx}, - { .id = 0x111d7666, .name = "92HD88B3", .patch = patch_stac92hd83xxx}, - { .id = 0x111d7667, .name = "92HD88B1", .patch = patch_stac92hd83xxx}, - { .id = 0x111d7668, .name = "92HD88B2", .patch = patch_stac92hd83xxx}, - { .id = 0x111d7669, .name = "92HD88B4", .patch = patch_stac92hd83xxx}, - { .id = 0x111d7608, .name = "92HD75B2X5", .patch = patch_stac92hd71bxx}, - { .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx }, - { .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx }, - { .id = 0x111d7676, .name = "92HD73E1X5", .patch = patch_stac92hd73xx }, - { .id = 0x111d76b0, .name = "92HD71B8X", .patch = patch_stac92hd71bxx }, - { .id = 0x111d76b1, .name = "92HD71B8X", .patch = patch_stac92hd71bxx }, - { .id = 0x111d76b2, .name = "92HD71B7X", .patch = patch_stac92hd71bxx }, - { .id = 0x111d76b3, .name = "92HD71B7X", .patch = patch_stac92hd71bxx }, - { .id = 0x111d76b4, .name = "92HD71B6X", .patch = patch_stac92hd71bxx }, - { .id = 0x111d76b5, .name = "92HD71B6X", .patch = patch_stac92hd71bxx }, - { .id = 0x111d76b6, .name = "92HD71B5X", .patch = patch_stac92hd71bxx }, - { .id = 0x111d76b7, .name = "92HD71B5X", .patch = patch_stac92hd71bxx }, - { .id = 0x111d76c0, .name = "92HD89C3", .patch = patch_stac92hd73xx }, - { .id = 0x111d76c1, .name = "92HD89C2", .patch = patch_stac92hd73xx }, - { .id = 0x111d76c2, .name = "92HD89C1", .patch = patch_stac92hd73xx }, - { .id = 0x111d76c3, .name = "92HD89B3", .patch = patch_stac92hd73xx }, - { .id = 0x111d76c4, .name = "92HD89B2", .patch = patch_stac92hd73xx }, - { .id = 0x111d76c5, .name = "92HD89B1", .patch = patch_stac92hd73xx }, - { .id = 0x111d76c6, .name = "92HD89E3", .patch = patch_stac92hd73xx }, - { .id = 0x111d76c7, .name = "92HD89E2", .patch = patch_stac92hd73xx }, - { .id = 0x111d76c8, .name = "92HD89E1", .patch = patch_stac92hd73xx }, - { .id = 0x111d76c9, .name = "92HD89D3", .patch = patch_stac92hd73xx }, - { .id = 0x111d76ca, .name = "92HD89D2", .patch = patch_stac92hd73xx }, - { .id = 0x111d76cb, .name = "92HD89D1", .patch = patch_stac92hd73xx }, - { .id = 0x111d76cc, .name = "92HD89F3", .patch = patch_stac92hd73xx }, - { .id = 0x111d76cd, .name = "92HD89F2", .patch = patch_stac92hd73xx }, - { .id = 0x111d76ce, .name = "92HD89F1", .patch = patch_stac92hd73xx }, - { .id = 0x111d76df, .name = "92HD93BXX", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76e0, .name = "92HD91BXX", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76e3, .name = "92HD98BXX", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76e5, .name = "92HD99BXX", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76e7, .name = "92HD90BXX", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76e8, .name = "92HD66B1X5", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76e9, .name = "92HD66B2X5", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76ea, .name = "92HD66B3X5", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76eb, .name = "92HD66C1X5", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76ec, .name = "92HD66C2X5", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76ed, .name = "92HD66C3X5", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76ee, .name = "92HD66B1X3", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76ef, .name = "92HD66B2X3", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76f0, .name = "92HD66B3X3", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76f1, .name = "92HD66C1X3", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76f2, .name = "92HD66C2X3", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76f3, .name = "92HD66C3/65", .patch = patch_stac92hd83xxx}, - {} /* 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/ANDROID_3.4.5/sound/pci/hda/patch_via.c b/ANDROID_3.4.5/sound/pci/hda/patch_via.c deleted file mode 100644 index 06214fdc..00000000 --- a/ANDROID_3.4.5/sound/pci/hda/patch_via.c +++ /dev/null @@ -1,3935 +0,0 @@ -/* - * Universal Interface for Intel High Definition Audio Codec - * - * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec - * - * (C) 2006-2009 VIA Technology, Inc. - * (C) 2006-2008 Takashi Iwai <tiwai@suse.de> - * - * 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 - */ - -/* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */ -/* */ -/* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */ -/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */ -/* 2006-08-02 Lydia Wang Add support to VT1709 codec */ -/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */ -/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */ -/* 2007-09-17 Lydia Wang Add VT1708B codec support */ -/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */ -/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */ -/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */ -/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */ -/* 2008-04-09 Lydia Wang Add Independent HP feature */ -/* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */ -/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */ -/* 2009-02-16 Logan Li Add support for VT1718S */ -/* 2009-03-13 Logan Li Add support for VT1716S */ -/* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */ -/* 2009-07-08 Lydia Wang Add support for VT2002P */ -/* 2009-07-21 Lydia Wang Add support for VT1812 */ -/* 2009-09-19 Lydia Wang Add support for VT1818S */ -/* */ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - - -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <sound/core.h> -#include <sound/asoundef.h> -#include "hda_codec.h" -#include "hda_local.h" -#include "hda_jack.h" - -/* Pin Widget NID */ -#define VT1708_HP_PIN_NID 0x20 -#define VT1708_CD_PIN_NID 0x24 - -enum VIA_HDA_CODEC { - UNKNOWN = -1, - VT1708, - VT1709_10CH, - VT1709_6CH, - VT1708B_8CH, - VT1708B_4CH, - VT1708S, - VT1708BCE, - VT1702, - VT1718S, - VT1716S, - VT2002P, - VT1812, - VT1802, - CODEC_TYPES, -}; - -#define VT2002P_COMPATIBLE(spec) \ - ((spec)->codec_type == VT2002P ||\ - (spec)->codec_type == VT1812 ||\ - (spec)->codec_type == VT1802) - -#define MAX_NID_PATH_DEPTH 5 - -/* output-path: DAC -> ... -> pin - * idx[] contains the source index number of the next widget; - * e.g. idx[0] is the index of the DAC selected by path[1] widget - * multi[] indicates whether it's a selector widget with multi-connectors - * (i.e. the connection selection is mandatory) - * vol_ctl and mute_ctl contains the NIDs for the assigned mixers - */ -struct nid_path { - int depth; - hda_nid_t path[MAX_NID_PATH_DEPTH]; - unsigned char idx[MAX_NID_PATH_DEPTH]; - unsigned char multi[MAX_NID_PATH_DEPTH]; - unsigned int vol_ctl; - unsigned int mute_ctl; -}; - -/* input-path */ -struct via_input { - hda_nid_t pin; /* input-pin or aa-mix */ - int adc_idx; /* ADC index to be used */ - int mux_idx; /* MUX index (if any) */ - const char *label; /* input-source label */ -}; - -#define VIA_MAX_ADCS 3 - -enum { - STREAM_MULTI_OUT = (1 << 0), - STREAM_INDEP_HP = (1 << 1), -}; - -struct via_spec { - /* codec parameterization */ - const struct snd_kcontrol_new *mixers[6]; - unsigned int num_mixers; - - const struct hda_verb *init_verbs[5]; - unsigned int num_iverbs; - - char stream_name_analog[32]; - char stream_name_hp[32]; - const struct hda_pcm_stream *stream_analog_playback; - const struct hda_pcm_stream *stream_analog_capture; - - char stream_name_digital[32]; - const struct hda_pcm_stream *stream_digital_playback; - const struct hda_pcm_stream *stream_digital_capture; - - /* playback */ - struct hda_multi_out multiout; - hda_nid_t slave_dig_outs[2]; - hda_nid_t hp_dac_nid; - hda_nid_t speaker_dac_nid; - int hp_indep_shared; /* indep HP-DAC is shared with side ch */ - int opened_streams; /* STREAM_* bits */ - int active_streams; /* STREAM_* bits */ - int aamix_mode; /* loopback is enabled for output-path? */ - - /* Output-paths: - * There are different output-paths depending on the setup. - * out_path, hp_path and speaker_path are primary paths. If both - * direct DAC and aa-loopback routes are available, these contain - * the former paths. Meanwhile *_mix_path contain the paths with - * loopback mixer. (Since the loopback is only for front channel, - * no out_mix_path for surround channels.) - * The HP output has another path, hp_indep_path, which is used in - * the independent-HP mode. - */ - struct nid_path out_path[HDA_SIDE + 1]; - struct nid_path out_mix_path; - struct nid_path hp_path; - struct nid_path hp_mix_path; - struct nid_path hp_indep_path; - struct nid_path speaker_path; - struct nid_path speaker_mix_path; - - /* capture */ - unsigned int num_adc_nids; - hda_nid_t adc_nids[VIA_MAX_ADCS]; - hda_nid_t mux_nids[VIA_MAX_ADCS]; - hda_nid_t aa_mix_nid; - hda_nid_t dig_in_nid; - - /* capture source */ - bool dyn_adc_switch; - int num_inputs; - struct via_input inputs[AUTO_CFG_MAX_INS + 1]; - unsigned int cur_mux[VIA_MAX_ADCS]; - - /* dynamic DAC switching */ - unsigned int cur_dac_stream_tag; - unsigned int cur_dac_format; - unsigned int cur_hp_stream_tag; - unsigned int cur_hp_format; - - /* dynamic ADC switching */ - hda_nid_t cur_adc; - unsigned int cur_adc_stream_tag; - unsigned int cur_adc_format; - - /* PCM information */ - struct hda_pcm pcm_rec[3]; - - /* dynamic controls, init_verbs and input_mux */ - struct auto_pin_cfg autocfg; - struct snd_array kctls; - hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; - - /* HP mode source */ - unsigned int hp_independent_mode; - unsigned int dmic_enabled; - unsigned int no_pin_power_ctl; - enum VIA_HDA_CODEC codec_type; - - /* analog low-power control */ - bool alc_mode; - - /* smart51 setup */ - unsigned int smart51_nums; - hda_nid_t smart51_pins[2]; - int smart51_idxs[2]; - const char *smart51_labels[2]; - unsigned int smart51_enabled; - - /* work to check hp jack state */ - struct hda_codec *codec; - struct delayed_work vt1708_hp_work; - int hp_work_active; - int vt1708_jack_detect; - int vt1708_hp_present; - - void (*set_widgets_power_state)(struct hda_codec *codec); - - struct hda_loopback_check loopback; - int num_loopbacks; - struct hda_amp_list loopback_list[8]; - - /* bind capture-volume */ - struct hda_bind_ctls *bind_cap_vol; - struct hda_bind_ctls *bind_cap_sw; - - struct mutex config_mutex; -}; - -static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); -static struct via_spec * via_new_spec(struct hda_codec *codec) -{ - struct via_spec *spec; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return NULL; - - mutex_init(&spec->config_mutex); - codec->spec = spec; - spec->codec = codec; - spec->codec_type = get_codec_type(codec); - /* VT1708BCE & VT1708S are almost same */ - if (spec->codec_type == VT1708BCE) - spec->codec_type = VT1708S; - return spec; -} - -static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) -{ - u32 vendor_id = codec->vendor_id; - u16 ven_id = vendor_id >> 16; - u16 dev_id = vendor_id & 0xffff; - enum VIA_HDA_CODEC codec_type; - - /* get codec type */ - if (ven_id != 0x1106) - codec_type = UNKNOWN; - else if (dev_id >= 0x1708 && dev_id <= 0x170b) - codec_type = VT1708; - else if (dev_id >= 0xe710 && dev_id <= 0xe713) - codec_type = VT1709_10CH; - else if (dev_id >= 0xe714 && dev_id <= 0xe717) - codec_type = VT1709_6CH; - else if (dev_id >= 0xe720 && dev_id <= 0xe723) { - codec_type = VT1708B_8CH; - if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7) - codec_type = VT1708BCE; - } else if (dev_id >= 0xe724 && dev_id <= 0xe727) - codec_type = VT1708B_4CH; - else if ((dev_id & 0xfff) == 0x397 - && (dev_id >> 12) < 8) - codec_type = VT1708S; - else if ((dev_id & 0xfff) == 0x398 - && (dev_id >> 12) < 8) - codec_type = VT1702; - else if ((dev_id & 0xfff) == 0x428 - && (dev_id >> 12) < 8) - codec_type = VT1718S; - else if (dev_id == 0x0433 || dev_id == 0xa721) - codec_type = VT1716S; - else if (dev_id == 0x0441 || dev_id == 0x4441) - codec_type = VT1718S; - else if (dev_id == 0x0438 || dev_id == 0x4438) - codec_type = VT2002P; - else if (dev_id == 0x0448) - codec_type = VT1812; - else if (dev_id == 0x0440) - codec_type = VT1708S; - else if ((dev_id & 0xfff) == 0x446) - codec_type = VT1802; - else - codec_type = UNKNOWN; - return codec_type; -}; - -#define VIA_JACK_EVENT 0x20 -#define VIA_HP_EVENT 0x01 -#define VIA_GPIO_EVENT 0x02 -#define VIA_LINE_EVENT 0x03 - -enum { - VIA_CTL_WIDGET_VOL, - VIA_CTL_WIDGET_MUTE, - VIA_CTL_WIDGET_ANALOG_MUTE, -}; - -static void analog_low_current_mode(struct hda_codec *codec); -static bool is_aa_path_mute(struct hda_codec *codec); - -#define hp_detect_with_aa(codec) \ - (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \ - !is_aa_path_mute(codec)) - -static void vt1708_stop_hp_work(struct via_spec *spec) -{ - if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) - return; - if (spec->hp_work_active) { - snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 1); - cancel_delayed_work_sync(&spec->vt1708_hp_work); - spec->hp_work_active = 0; - } -} - -static void vt1708_update_hp_work(struct via_spec *spec) -{ - if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) - return; - if (spec->vt1708_jack_detect && - (spec->active_streams || hp_detect_with_aa(spec->codec))) { - if (!spec->hp_work_active) { - snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 0); - schedule_delayed_work(&spec->vt1708_hp_work, - msecs_to_jiffies(100)); - spec->hp_work_active = 1; - } - } else if (!hp_detect_with_aa(spec->codec)) - vt1708_stop_hp_work(spec); -} - -static void set_widgets_power_state(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - if (spec->set_widgets_power_state) - spec->set_widgets_power_state(codec); -} - -static int analog_input_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - - set_widgets_power_state(codec); - analog_low_current_mode(snd_kcontrol_chip(kcontrol)); - vt1708_update_hp_work(codec->spec); - return change; -} - -/* modify .put = snd_hda_mixer_amp_switch_put */ -#define ANALOG_INPUT_MUTE \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = NULL, \ - .index = 0, \ - .info = snd_hda_mixer_amp_switch_info, \ - .get = snd_hda_mixer_amp_switch_get, \ - .put = analog_input_switch_put, \ - .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } - -static const struct snd_kcontrol_new via_control_templates[] = { - HDA_CODEC_VOLUME(NULL, 0, 0, 0), - HDA_CODEC_MUTE(NULL, 0, 0, 0), - ANALOG_INPUT_MUTE, -}; - - -/* add dynamic controls */ -static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec, - const struct snd_kcontrol_new *tmpl, - const char *name) -{ - struct snd_kcontrol_new *knew; - - snd_array_init(&spec->kctls, sizeof(*knew), 32); - knew = snd_array_new(&spec->kctls); - if (!knew) - return NULL; - *knew = *tmpl; - if (!name) - name = tmpl->name; - if (name) { - knew->name = kstrdup(name, GFP_KERNEL); - if (!knew->name) - return NULL; - } - return knew; -} - -static int __via_add_control(struct via_spec *spec, int type, const char *name, - int idx, unsigned long val) -{ - struct snd_kcontrol_new *knew; - - knew = __via_clone_ctl(spec, &via_control_templates[type], name); - if (!knew) - return -ENOMEM; - knew->index = idx; - if (get_amp_nid_(val)) - knew->subdevice = HDA_SUBDEV_AMP_FLAG; - knew->private_value = val; - return 0; -} - -#define via_add_control(spec, type, name, val) \ - __via_add_control(spec, type, name, 0, val) - -#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL) - -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, const char *ctlname, - int type_idx, int idx, int mix_nid) -{ - char name[32]; - int err; - - sprintf(name, "%s Playback Volume", ctlname); - err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx, - HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", ctlname); - err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx, - HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); - if (err < 0) - return err; - return 0; -} - -#define get_connection_index(codec, mux, nid) \ - snd_hda_get_conn_index(codec, mux, nid, 0) - -static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, - unsigned int mask) -{ - unsigned int caps; - if (!nid) - return false; - caps = get_wcaps(codec, nid); - if (dir == HDA_INPUT) - caps &= AC_WCAP_IN_AMP; - else - caps &= AC_WCAP_OUT_AMP; - if (!caps) - return false; - if (query_amp_caps(codec, nid, dir) & mask) - return true; - return false; -} - -#define have_mute(codec, nid, dir) \ - check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE) - -/* enable/disable the output-route mixers */ -static void activate_output_mix(struct hda_codec *codec, struct nid_path *path, - hda_nid_t mix_nid, int idx, bool enable) -{ - int i, num, val; - - if (!path) - return; - num = snd_hda_get_conn_list(codec, mix_nid, NULL); - for (i = 0; i < num; i++) { - if (i == idx) - val = AMP_IN_UNMUTE(i); - else - val = AMP_IN_MUTE(i); - snd_hda_codec_write(codec, mix_nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, val); - } -} - -/* enable/disable the output-route */ -static void activate_output_path(struct hda_codec *codec, struct nid_path *path, - bool enable, bool force) -{ - struct via_spec *spec = codec->spec; - int i; - for (i = 0; i < path->depth; i++) { - hda_nid_t src, dst; - int idx = path->idx[i]; - src = path->path[i]; - if (i < path->depth - 1) - dst = path->path[i + 1]; - else - dst = 0; - if (enable && path->multi[i]) - snd_hda_codec_write(codec, dst, 0, - AC_VERB_SET_CONNECT_SEL, idx); - if (!force && (dst == spec->aa_mix_nid)) - continue; - if (have_mute(codec, dst, HDA_INPUT)) - activate_output_mix(codec, path, dst, idx, enable); - if (!force && (src == path->vol_ctl || src == path->mute_ctl)) - continue; - if (have_mute(codec, src, HDA_OUTPUT)) { - int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE; - snd_hda_codec_write(codec, src, 0, - AC_VERB_SET_AMP_GAIN_MUTE, val); - } - } -} - -/* set the given pin as output */ -static void init_output_pin(struct hda_codec *codec, hda_nid_t pin, - int pin_type) -{ - if (!pin) - return; - snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pin_type); - if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD) - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_EAPD_BTLENABLE, 0x02); -} - -static void via_auto_init_output(struct hda_codec *codec, - struct nid_path *path, int pin_type) -{ - unsigned int caps; - hda_nid_t pin; - - if (!path->depth) - return; - pin = path->path[path->depth - 1]; - - init_output_pin(codec, pin, pin_type); - if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP) - caps = query_amp_caps(codec, pin, HDA_OUTPUT); - else - caps = 0; - if (caps & AC_AMPCAP_MUTE) { - unsigned int val; - val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; - snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_MUTE | val); - } - activate_output_path(codec, path, true, true); /* force on */ -} - -static void via_auto_init_multi_out(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - struct nid_path *path; - int i; - - for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) { - path = &spec->out_path[i]; - if (!i && spec->aamix_mode && spec->out_mix_path.depth) - path = &spec->out_mix_path; - via_auto_init_output(codec, path, PIN_OUT); - } -} - -/* deactivate the inactive headphone-paths */ -static void deactivate_hp_paths(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int shared = spec->hp_indep_shared; - - if (spec->hp_independent_mode) { - activate_output_path(codec, &spec->hp_path, false, false); - activate_output_path(codec, &spec->hp_mix_path, false, false); - if (shared) - activate_output_path(codec, &spec->out_path[shared], - false, false); - } else if (spec->aamix_mode || !spec->hp_path.depth) { - activate_output_path(codec, &spec->hp_indep_path, false, false); - activate_output_path(codec, &spec->hp_path, false, false); - } else { - activate_output_path(codec, &spec->hp_indep_path, false, false); - activate_output_path(codec, &spec->hp_mix_path, false, false); - } -} - -static void via_auto_init_hp_out(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - - if (!spec->hp_path.depth) { - via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP); - return; - } - deactivate_hp_paths(codec); - if (spec->hp_independent_mode) - via_auto_init_output(codec, &spec->hp_indep_path, PIN_HP); - else if (spec->aamix_mode) - via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP); - else - via_auto_init_output(codec, &spec->hp_path, PIN_HP); -} - -static void via_auto_init_speaker_out(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - - if (!spec->autocfg.speaker_outs) - return; - if (!spec->speaker_path.depth) { - via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT); - return; - } - if (!spec->aamix_mode) { - activate_output_path(codec, &spec->speaker_mix_path, - false, false); - via_auto_init_output(codec, &spec->speaker_path, PIN_OUT); - } else { - activate_output_path(codec, &spec->speaker_path, false, false); - via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT); - } -} - -static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin); -static void via_hp_automute(struct hda_codec *codec); - -static void via_auto_init_analog_input(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t conn[HDA_MAX_CONNECTIONS]; - unsigned int ctl; - int i, num_conns; - - /* init ADCs */ - for (i = 0; i < spec->num_adc_nids; i++) { - hda_nid_t nid = spec->adc_nids[i]; - if (!(get_wcaps(codec, nid) & AC_WCAP_IN_AMP) || - !(query_amp_caps(codec, nid, HDA_INPUT) & AC_AMPCAP_MUTE)) - continue; - snd_hda_codec_write(codec, spec->adc_nids[i], 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(0)); - } - - /* init pins */ - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - if (spec->smart51_enabled && is_smart51_pins(codec, nid)) - ctl = PIN_OUT; - else if (cfg->inputs[i].type == AUTO_PIN_MIC) - ctl = PIN_VREF50; - else - ctl = PIN_IN; - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, ctl); - } - - /* init input-src */ - for (i = 0; i < spec->num_adc_nids; i++) { - int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx; - /* secondary ADCs must have the unique MUX */ - if (i > 0 && !spec->mux_nids[i]) - break; - if (spec->mux_nids[adc_idx]) { - int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx; - snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, - AC_VERB_SET_CONNECT_SEL, - mux_idx); - } - if (spec->dyn_adc_switch) - break; /* only one input-src */ - } - - /* init aa-mixer */ - if (!spec->aa_mix_nid) - return; - num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn, - ARRAY_SIZE(conn)); - for (i = 0; i < num_conns; i++) { - unsigned int caps = get_wcaps(codec, conn[i]); - if (get_wcaps_type(caps) == AC_WID_PIN) - snd_hda_codec_write(codec, spec->aa_mix_nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_MUTE(i)); - } -} - -static void update_power_state(struct hda_codec *codec, hda_nid_t nid, - unsigned int parm) -{ - if (snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_POWER_STATE, 0) == parm) - return; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); -} - -static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, - unsigned int *affected_parm) -{ - unsigned parm; - unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid); - unsigned no_presence = (def_conf & AC_DEFCFG_MISC) - >> AC_DEFCFG_MISC_SHIFT - & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ - struct via_spec *spec = codec->spec; - unsigned present = 0; - - no_presence |= spec->no_pin_power_ctl; - if (!no_presence) - present = snd_hda_jack_detect(codec, nid); - if ((spec->smart51_enabled && is_smart51_pins(codec, nid)) - || ((no_presence || present) - && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) { - *affected_parm = AC_PWRST_D0; /* if it's connected */ - parm = AC_PWRST_D0; - } else - parm = AC_PWRST_D3; - - update_power_state(codec, nid, parm); -} - -static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - static const char * const texts[] = { - "Disabled", "Enabled" - }; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; -} - -static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl; - return 0; -} - -static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - unsigned int val = !ucontrol->value.enumerated.item[0]; - - if (val == spec->no_pin_power_ctl) - return 0; - spec->no_pin_power_ctl = val; - set_widgets_power_state(codec); - analog_low_current_mode(codec); - return 1; -} - -static const struct snd_kcontrol_new via_pin_power_ctl_enum = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Dynamic Power-Control", - .info = via_pin_power_ctl_info, - .get = via_pin_power_ctl_get, - .put = via_pin_power_ctl_put, -}; - - -static int via_independent_hp_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - static const char * const texts[] = { "OFF", "ON" }; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item >= 2) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; -} - -static int via_independent_hp_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - - ucontrol->value.enumerated.item[0] = spec->hp_independent_mode; - return 0; -} - -/* adjust spec->multiout setup according to the current flags */ -static void setup_playback_multi_pcm(struct via_spec *spec) -{ - const struct auto_pin_cfg *cfg = &spec->autocfg; - spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums; - spec->multiout.hp_nid = 0; - if (!spec->hp_independent_mode) { - if (!spec->hp_indep_shared) - spec->multiout.hp_nid = spec->hp_dac_nid; - } else { - if (spec->hp_indep_shared) - spec->multiout.num_dacs = cfg->line_outs - 1; - } -} - -/* update DAC setups according to indep-HP switch; - * this function is called only when indep-HP is modified - */ -static void switch_indep_hp_dacs(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int shared = spec->hp_indep_shared; - hda_nid_t shared_dac, hp_dac; - - if (!spec->opened_streams) - return; - - shared_dac = shared ? spec->multiout.dac_nids[shared] : 0; - hp_dac = spec->hp_dac_nid; - if (spec->hp_independent_mode) { - /* switch to indep-HP mode */ - if (spec->active_streams & STREAM_MULTI_OUT) { - __snd_hda_codec_cleanup_stream(codec, hp_dac, 1); - __snd_hda_codec_cleanup_stream(codec, shared_dac, 1); - } - if (spec->active_streams & STREAM_INDEP_HP) - snd_hda_codec_setup_stream(codec, hp_dac, - spec->cur_hp_stream_tag, 0, - spec->cur_hp_format); - } else { - /* back to HP or shared-DAC */ - if (spec->active_streams & STREAM_INDEP_HP) - __snd_hda_codec_cleanup_stream(codec, hp_dac, 1); - if (spec->active_streams & STREAM_MULTI_OUT) { - hda_nid_t dac; - int ch; - if (shared_dac) { /* reset mutli-ch DAC */ - dac = shared_dac; - ch = shared * 2; - } else { /* reset HP DAC */ - dac = hp_dac; - ch = 0; - } - snd_hda_codec_setup_stream(codec, dac, - spec->cur_dac_stream_tag, ch, - spec->cur_dac_format); - } - } - setup_playback_multi_pcm(spec); -} - -static int via_independent_hp_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - int cur, shared; - - mutex_lock(&spec->config_mutex); - cur = !!ucontrol->value.enumerated.item[0]; - if (spec->hp_independent_mode == cur) { - mutex_unlock(&spec->config_mutex); - return 0; - } - spec->hp_independent_mode = cur; - shared = spec->hp_indep_shared; - deactivate_hp_paths(codec); - if (cur) - activate_output_path(codec, &spec->hp_indep_path, true, false); - else { - if (shared) - activate_output_path(codec, &spec->out_path[shared], - true, false); - if (spec->aamix_mode || !spec->hp_path.depth) - activate_output_path(codec, &spec->hp_mix_path, - true, false); - else - activate_output_path(codec, &spec->hp_path, - true, false); - } - - switch_indep_hp_dacs(codec); - mutex_unlock(&spec->config_mutex); - - /* update jack power state */ - set_widgets_power_state(codec); - via_hp_automute(codec); - return 1; -} - -static const struct snd_kcontrol_new via_hp_mixer = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Independent HP", - .info = via_independent_hp_info, - .get = via_independent_hp_get, - .put = via_independent_hp_put, -}; - -static int via_hp_build(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - struct snd_kcontrol_new *knew; - hda_nid_t nid; - - nid = spec->autocfg.hp_pins[0]; - knew = via_clone_control(spec, &via_hp_mixer); - if (knew == NULL) - return -ENOMEM; - - knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; - - return 0; -} - -static void notify_aa_path_ctls(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->smart51_nums; i++) { - struct snd_kcontrol *ctl; - struct snd_ctl_elem_id id; - memset(&id, 0, sizeof(id)); - id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]); - ctl = snd_hda_find_mixer_ctl(codec, id.name); - if (ctl) - snd_ctl_notify(codec->bus->card, - SNDRV_CTL_EVENT_MASK_VALUE, - &ctl->id); - } -} - -static void mute_aa_path(struct hda_codec *codec, int mute) -{ - struct via_spec *spec = codec->spec; - int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE; - int i; - - /* check AA path's mute status */ - for (i = 0; i < spec->smart51_nums; i++) { - if (spec->smart51_idxs[i] < 0) - continue; - snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid, - HDA_INPUT, spec->smart51_idxs[i], - HDA_AMP_MUTE, val); - } -} - -static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin) -{ - struct via_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->smart51_nums; i++) - if (spec->smart51_pins[i] == pin) - return true; - return false; -} - -static int via_smart51_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - - *ucontrol->value.integer.value = spec->smart51_enabled; - return 0; -} - -static int via_smart51_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - int out_in = *ucontrol->value.integer.value - ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN; - int i; - - for (i = 0; i < spec->smart51_nums; i++) { - hda_nid_t nid = spec->smart51_pins[i]; - unsigned int parm; - - parm = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); - parm |= out_in; - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - parm); - if (out_in == AC_PINCTL_OUT_EN) { - mute_aa_path(codec, 1); - notify_aa_path_ctls(codec); - } - } - spec->smart51_enabled = *ucontrol->value.integer.value; - set_widgets_power_state(codec); - return 1; -} - -static const struct snd_kcontrol_new via_smart51_mixer = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Smart 5.1", - .count = 1, - .info = snd_ctl_boolean_mono_info, - .get = via_smart51_get, - .put = via_smart51_put, -}; - -static int via_smart51_build(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - - if (!spec->smart51_nums) - return 0; - if (!via_clone_control(spec, &via_smart51_mixer)) - return -ENOMEM; - return 0; -} - -/* check AA path's mute status */ -static bool is_aa_path_mute(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - const struct hda_amp_list *p; - int i, ch, v; - - for (i = 0; i < spec->num_loopbacks; i++) { - p = &spec->loopback_list[i]; - for (ch = 0; ch < 2; ch++) { - v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir, - p->idx); - if (!(v & HDA_AMP_MUTE) && v > 0) - return false; - } - } - return true; -} - -/* enter/exit analog low-current mode */ -static void __analog_low_current_mode(struct hda_codec *codec, bool force) -{ - struct via_spec *spec = codec->spec; - bool enable; - unsigned int verb, parm; - - if (spec->no_pin_power_ctl) - enable = false; - else - enable = is_aa_path_mute(codec) && !spec->opened_streams; - if (enable == spec->alc_mode && !force) - return; - spec->alc_mode = enable; - - /* decide low current mode's verb & parameter */ - switch (spec->codec_type) { - case VT1708B_8CH: - case VT1708B_4CH: - verb = 0xf70; - parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ - break; - case VT1708S: - case VT1718S: - case VT1716S: - verb = 0xf73; - parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ - break; - case VT1702: - verb = 0xf73; - parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ - break; - case VT2002P: - case VT1812: - case VT1802: - verb = 0xf93; - parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ - break; - default: - return; /* other codecs are not supported */ - } - /* send verb */ - snd_hda_codec_write(codec, codec->afg, 0, verb, parm); -} - -static void analog_low_current_mode(struct hda_codec *codec) -{ - return __analog_low_current_mode(codec, false); -} - -/* - * generic initialization of ADC, input mixers and output mixers - */ -static const struct hda_verb vt1708_init_verbs[] = { - /* power down jack detect function */ - {0x1, 0xf81, 0x1}, - { } -}; - -static void set_stream_open(struct hda_codec *codec, int bit, bool active) -{ - struct via_spec *spec = codec->spec; - - if (active) - spec->opened_streams |= bit; - else - spec->opened_streams &= ~bit; - analog_low_current_mode(codec); -} - -static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; - int err; - - spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums; - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - set_stream_open(codec, STREAM_MULTI_OUT, true); - err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, - hinfo); - if (err < 0) { - set_stream_open(codec, STREAM_MULTI_OUT, false); - return err; - } - return 0; -} - -static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - set_stream_open(codec, STREAM_MULTI_OUT, false); - return 0; -} - -static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - - if (snd_BUG_ON(!spec->hp_dac_nid)) - return -EINVAL; - set_stream_open(codec, STREAM_INDEP_HP, true); - return 0; -} - -static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - set_stream_open(codec, STREAM_INDEP_HP, false); - return 0; -} - -static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - - mutex_lock(&spec->config_mutex); - setup_playback_multi_pcm(spec); - snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, - format, substream); - /* remember for dynamic DAC switch with indep-HP */ - spec->active_streams |= STREAM_MULTI_OUT; - spec->cur_dac_stream_tag = stream_tag; - spec->cur_dac_format = format; - mutex_unlock(&spec->config_mutex); - vt1708_update_hp_work(spec); - return 0; -} - -static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - - mutex_lock(&spec->config_mutex); - if (spec->hp_independent_mode) - snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, - stream_tag, 0, format); - spec->active_streams |= STREAM_INDEP_HP; - spec->cur_hp_stream_tag = stream_tag; - spec->cur_hp_format = format; - mutex_unlock(&spec->config_mutex); - vt1708_update_hp_work(spec); - return 0; -} - -static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - - mutex_lock(&spec->config_mutex); - snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); - spec->active_streams &= ~STREAM_MULTI_OUT; - mutex_unlock(&spec->config_mutex); - vt1708_update_hp_work(spec); - return 0; -} - -static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - - mutex_lock(&spec->config_mutex); - if (spec->hp_independent_mode) - snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0); - spec->active_streams &= ~STREAM_INDEP_HP; - mutex_unlock(&spec->config_mutex); - vt1708_update_hp_work(spec); - return 0; -} - -/* - * Digital out - */ -static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - return snd_hda_multi_out_dig_open(codec, &spec->multiout); -} - -static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} - -static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, - stream_tag, format, substream); -} - -static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); - return 0; -} - -/* - * Analog capture - */ -static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], - stream_tag, 0, format); - return 0; -} - -static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); - return 0; -} - -/* analog capture with dynamic ADC switching */ -static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx; - - mutex_lock(&spec->config_mutex); - spec->cur_adc = spec->adc_nids[adc_idx]; - spec->cur_adc_stream_tag = stream_tag; - spec->cur_adc_format = format; - snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format); - mutex_unlock(&spec->config_mutex); - return 0; -} - -static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - - mutex_lock(&spec->config_mutex); - snd_hda_codec_cleanup_stream(codec, spec->cur_adc); - spec->cur_adc = 0; - mutex_unlock(&spec->config_mutex); - return 0; -} - -/* re-setup the stream if running; called from input-src put */ -static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur) -{ - struct via_spec *spec = codec->spec; - int adc_idx = spec->inputs[cur].adc_idx; - hda_nid_t adc = spec->adc_nids[adc_idx]; - bool ret = false; - - mutex_lock(&spec->config_mutex); - if (spec->cur_adc && spec->cur_adc != adc) { - /* stream is running, let's swap the current ADC */ - __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); - spec->cur_adc = adc; - snd_hda_codec_setup_stream(codec, adc, - spec->cur_adc_stream_tag, 0, - spec->cur_adc_format); - ret = true; - } - mutex_unlock(&spec->config_mutex); - return ret; -} - -static const struct hda_pcm_stream via_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 8, - /* NID is set in via_build_pcms */ - .ops = { - .open = via_playback_multi_pcm_open, - .close = via_playback_multi_pcm_close, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream via_pcm_hp_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .open = via_playback_hp_pcm_open, - .close = via_playback_hp_pcm_close, - .prepare = via_playback_hp_pcm_prepare, - .cleanup = via_playback_hp_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 8, - /* NID is set in via_build_pcms */ - /* We got noisy outputs on the right channel on VT1708 when - * 24bit samples are used. Until any workaround is found, - * disable the 24bit format, so far. - */ - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .ops = { - .open = via_playback_multi_pcm_open, - .close = via_playback_multi_pcm_close, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream via_pcm_analog_capture = { - .substreams = 1, /* will be changed in via_build_pcms() */ - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .prepare = via_dyn_adc_capture_pcm_prepare, - .cleanup = via_dyn_adc_capture_pcm_cleanup, - }, -}; - -static const struct hda_pcm_stream via_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .open = via_dig_playback_pcm_open, - .close = via_dig_playback_pcm_close, - .prepare = via_dig_playback_pcm_prepare, - .cleanup = via_dig_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream via_pcm_digital_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, -}; - -/* - * slave controls for virtual master - */ -static const char * const via_slave_pfxs[] = { - "Front", "Surround", "Center", "LFE", "Side", - "Headphone", "Speaker", - NULL, -}; - -static int via_build_controls(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - struct snd_kcontrol *kctl; - int err, i; - - spec->no_pin_power_ctl = 1; - if (spec->set_widgets_power_state) - if (!via_clone_control(spec, &via_pin_power_ctl_enum)) - return -ENOMEM; - - for (i = 0; i < spec->num_mixers; i++) { - err = snd_hda_add_new_ctls(codec, spec->mixers[i]); - if (err < 0) - return err; - } - - if (spec->multiout.dig_out_nid) { - err = snd_hda_create_spdif_out_ctls(codec, - spec->multiout.dig_out_nid, - spec->multiout.dig_out_nid); - if (err < 0) - return err; - err = snd_hda_create_spdif_share_sw(codec, - &spec->multiout); - if (err < 0) - return err; - spec->multiout.share_spdif = 1; - } - if (spec->dig_in_nid) { - err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); - if (err < 0) - return err; - } - - /* if we have no master control, let's create it */ - if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { - unsigned int vmaster_tlv[4]; - snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0], - HDA_OUTPUT, vmaster_tlv); - err = snd_hda_add_vmaster(codec, "Master Playback Volume", - vmaster_tlv, via_slave_pfxs, - "Playback Volume"); - if (err < 0) - return err; - } - if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { - err = snd_hda_add_vmaster(codec, "Master Playback Switch", - NULL, via_slave_pfxs, - "Playback Switch"); - if (err < 0) - return err; - } - - /* assign Capture Source enums to NID */ - kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); - for (i = 0; kctl && i < kctl->count; i++) { - if (!spec->mux_nids[i]) - continue; - err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]); - if (err < 0) - return err; - } - - via_free_kctls(codec); /* no longer needed */ - - err = snd_hda_jack_add_kctls(codec, &spec->autocfg); - if (err < 0) - return err; - - return 0; -} - -static int via_build_pcms(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; - - codec->num_pcms = 0; - codec->pcm_info = info; - - if (spec->multiout.num_dacs || spec->num_adc_nids) { - snprintf(spec->stream_name_analog, - sizeof(spec->stream_name_analog), - "%s Analog", codec->chip_name); - info->name = spec->stream_name_analog; - - if (spec->multiout.num_dacs) { - if (!spec->stream_analog_playback) - spec->stream_analog_playback = - &via_pcm_analog_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = - *spec->stream_analog_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = - spec->multiout.dac_nids[0]; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = - spec->multiout.max_channels; - } - - if (!spec->stream_analog_capture) { - if (spec->dyn_adc_switch) - spec->stream_analog_capture = - &via_pcm_dyn_adc_analog_capture; - else - spec->stream_analog_capture = - &via_pcm_analog_capture; - } - if (spec->num_adc_nids) { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = - *spec->stream_analog_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = - spec->adc_nids[0]; - if (!spec->dyn_adc_switch) - info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = - spec->num_adc_nids; - } - codec->num_pcms++; - info++; - } - - if (spec->multiout.dig_out_nid || spec->dig_in_nid) { - snprintf(spec->stream_name_digital, - sizeof(spec->stream_name_digital), - "%s Digital", codec->chip_name); - info->name = spec->stream_name_digital; - info->pcm_type = HDA_PCM_TYPE_SPDIF; - if (spec->multiout.dig_out_nid) { - if (!spec->stream_digital_playback) - spec->stream_digital_playback = - &via_pcm_digital_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = - *spec->stream_digital_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = - spec->multiout.dig_out_nid; - } - if (spec->dig_in_nid) { - if (!spec->stream_digital_capture) - spec->stream_digital_capture = - &via_pcm_digital_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = - *spec->stream_digital_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = - spec->dig_in_nid; - } - codec->num_pcms++; - info++; - } - - if (spec->hp_dac_nid) { - snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp), - "%s HP", codec->chip_name); - info->name = spec->stream_name_hp; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = - spec->hp_dac_nid; - codec->num_pcms++; - info++; - } - return 0; -} - -static void via_free(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - - if (!spec) - return; - - via_free_kctls(codec); - vt1708_stop_hp_work(spec); - kfree(spec->bind_cap_vol); - kfree(spec->bind_cap_sw); - kfree(spec); -} - -/* mute/unmute outputs */ -static void toggle_output_mutes(struct hda_codec *codec, int num_pins, - hda_nid_t *pins, bool mute) -{ - int i; - for (i = 0; i < num_pins; i++) { - unsigned int parm = snd_hda_codec_read(codec, pins[i], 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - if (parm & AC_PINCTL_IN_EN) - continue; - if (mute) - parm &= ~AC_PINCTL_OUT_EN; - else - parm |= AC_PINCTL_OUT_EN; - snd_hda_codec_write(codec, pins[i], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, parm); - } -} - -/* mute internal speaker if line-out is plugged */ -static void via_line_automute(struct hda_codec *codec, int present) -{ - struct via_spec *spec = codec->spec; - - if (!spec->autocfg.speaker_outs) - return; - if (!present) - present = snd_hda_jack_detect(codec, - spec->autocfg.line_out_pins[0]); - toggle_output_mutes(codec, spec->autocfg.speaker_outs, - spec->autocfg.speaker_pins, - present); -} - -/* mute internal speaker if HP is plugged */ -static void via_hp_automute(struct hda_codec *codec) -{ - int present = 0; - int nums; - struct via_spec *spec = codec->spec; - - if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0] && - (spec->codec_type != VT1708 || spec->vt1708_jack_detect)) - present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); - - if (spec->smart51_enabled) - nums = spec->autocfg.line_outs + spec->smart51_nums; - else - nums = spec->autocfg.line_outs; - toggle_output_mutes(codec, nums, spec->autocfg.line_out_pins, present); - - via_line_automute(codec, present); -} - -static void via_gpio_control(struct hda_codec *codec) -{ - unsigned int gpio_data; - unsigned int vol_counter; - unsigned int vol; - unsigned int master_vol; - - struct via_spec *spec = codec->spec; - - gpio_data = snd_hda_codec_read(codec, codec->afg, 0, - AC_VERB_GET_GPIO_DATA, 0) & 0x03; - - vol_counter = (snd_hda_codec_read(codec, codec->afg, 0, - 0xF84, 0) & 0x3F0000) >> 16; - - vol = vol_counter & 0x1F; - master_vol = snd_hda_codec_read(codec, 0x1A, 0, - AC_VERB_GET_AMP_GAIN_MUTE, - AC_AMP_GET_INPUT); - - if (gpio_data == 0x02) { - /* unmute line out */ - snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - PIN_OUT); - if (vol_counter & 0x20) { - /* decrease volume */ - if (vol > master_vol) - vol = master_vol; - snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, - 0, HDA_AMP_VOLMASK, - master_vol-vol); - } else { - /* increase volume */ - snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0, - HDA_AMP_VOLMASK, - ((master_vol+vol) > 0x2A) ? 0x2A : - (master_vol+vol)); - } - } else if (!(gpio_data & 0x02)) { - /* mute line out */ - snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - 0); - } -} - -/* unsolicited event for jack sensing */ -static void via_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - res >>= 26; - res = snd_hda_jack_get_action(codec, res); - - if (res & VIA_JACK_EVENT) - set_widgets_power_state(codec); - - res &= ~VIA_JACK_EVENT; - - if (res == VIA_HP_EVENT || res == VIA_LINE_EVENT) - via_hp_automute(codec); - else if (res == VIA_GPIO_EVENT) - via_gpio_control(codec); - snd_hda_jack_report_sync(codec); -} - -#ifdef CONFIG_PM -static int via_suspend(struct hda_codec *codec, pm_message_t state) -{ - struct via_spec *spec = codec->spec; - vt1708_stop_hp_work(spec); - return 0; -} -#endif - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) -{ - struct via_spec *spec = codec->spec; - return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); -} -#endif - -/* - */ - -static int via_init(struct hda_codec *codec); - -static const struct hda_codec_ops via_patch_ops = { - .build_controls = via_build_controls, - .build_pcms = via_build_pcms, - .init = via_init, - .free = via_free, - .unsol_event = via_unsol_event, -#ifdef CONFIG_PM - .suspend = via_suspend, -#endif -#ifdef CONFIG_SND_HDA_POWER_SAVE - .check_power_status = via_check_power_status, -#endif -}; - -static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac) -{ - struct via_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->multiout.num_dacs; i++) { - if (spec->multiout.dac_nids[i] == dac) - return false; - } - if (spec->hp_dac_nid == dac) - return false; - return true; -} - -static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t target_dac, int with_aa_mix, - struct nid_path *path, int depth) -{ - struct via_spec *spec = codec->spec; - hda_nid_t conn[8]; - int i, nums; - - if (nid == spec->aa_mix_nid) { - if (!with_aa_mix) - return false; - with_aa_mix = 2; /* mark aa-mix is included */ - } - - nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); - for (i = 0; i < nums; i++) { - if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT) - continue; - if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) { - /* aa-mix is requested but not included? */ - if (!(spec->aa_mix_nid && with_aa_mix == 1)) - goto found; - } - } - if (depth >= MAX_NID_PATH_DEPTH) - return false; - for (i = 0; i < nums; i++) { - unsigned int type; - type = get_wcaps_type(get_wcaps(codec, conn[i])); - if (type == AC_WID_AUD_OUT) - continue; - if (__parse_output_path(codec, conn[i], target_dac, - with_aa_mix, path, depth + 1)) - goto found; - } - return false; - - found: - path->path[path->depth] = conn[i]; - path->idx[path->depth] = i; - if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX) - path->multi[path->depth] = 1; - path->depth++; - return true; -} - -static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t target_dac, int with_aa_mix, - struct nid_path *path) -{ - if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) { - path->path[path->depth] = nid; - path->depth++; - snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n", - path->depth, path->path[0], path->path[1], - path->path[2], path->path[3], path->path[4]); - return true; - } - return false; -} - -static int via_auto_fill_dac_nids(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; - int i, dac_num; - hda_nid_t nid; - - spec->multiout.dac_nids = spec->private_dac_nids; - dac_num = 0; - for (i = 0; i < cfg->line_outs; i++) { - hda_nid_t dac = 0; - nid = cfg->line_out_pins[i]; - if (!nid) - continue; - if (parse_output_path(codec, nid, 0, 0, &spec->out_path[i])) - dac = spec->out_path[i].path[0]; - if (!i && parse_output_path(codec, nid, dac, 1, - &spec->out_mix_path)) - dac = spec->out_mix_path.path[0]; - if (dac) { - spec->private_dac_nids[i] = dac; - dac_num++; - } - } - if (!spec->out_path[0].depth && spec->out_mix_path.depth) { - spec->out_path[0] = spec->out_mix_path; - spec->out_mix_path.depth = 0; - } - spec->multiout.num_dacs = dac_num; - return 0; -} - -static int create_ch_ctls(struct hda_codec *codec, const char *pfx, - int chs, bool check_dac, struct nid_path *path) -{ - struct via_spec *spec = codec->spec; - char name[32]; - hda_nid_t dac, pin, sel, nid; - int err; - - dac = check_dac ? path->path[0] : 0; - pin = path->path[path->depth - 1]; - sel = path->depth > 1 ? path->path[1] : 0; - - if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS)) - nid = dac; - else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS)) - nid = pin; - else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS)) - nid = sel; - else - nid = 0; - if (nid) { - sprintf(name, "%s Playback Volume", pfx); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); - if (err < 0) - return err; - path->vol_ctl = nid; - } - - if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE)) - nid = dac; - else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE)) - nid = pin; - else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE)) - nid = sel; - else - nid = 0; - if (nid) { - sprintf(name, "%s Playback Switch", pfx); - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); - if (err < 0) - return err; - path->mute_ctl = nid; - } - return 0; -} - -static void mangle_smart51(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - struct auto_pin_cfg_item *ins = cfg->inputs; - int i, j, nums, attr; - int pins[AUTO_CFG_MAX_INS]; - - for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) { - nums = 0; - for (i = 0; i < cfg->num_inputs; i++) { - unsigned int def; - if (ins[i].type > AUTO_PIN_LINE_IN) - continue; - def = snd_hda_codec_get_pincfg(codec, ins[i].pin); - if (snd_hda_get_input_pin_attr(def) != attr) - continue; - for (j = 0; j < nums; j++) - if (ins[pins[j]].type < ins[i].type) { - memmove(pins + j + 1, pins + j, - (nums - j) * sizeof(int)); - break; - } - pins[j] = i; - nums++; - } - if (cfg->line_outs + nums < 3) - continue; - for (i = 0; i < nums; i++) { - hda_nid_t pin = ins[pins[i]].pin; - spec->smart51_pins[spec->smart51_nums++] = pin; - cfg->line_out_pins[cfg->line_outs++] = pin; - if (cfg->line_outs == 3) - break; - } - return; - } -} - -static void copy_path_mixer_ctls(struct nid_path *dst, struct nid_path *src) -{ - dst->vol_ctl = src->vol_ctl; - dst->mute_ctl = src->mute_ctl; -} - -/* add playback controls from the parsed DAC table */ -static int via_auto_create_multi_out_ctls(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - struct nid_path *path; - static const char * const chname[4] = { - "Front", "Surround", "C/LFE", "Side" - }; - int i, idx, err; - int old_line_outs; - - /* check smart51 */ - old_line_outs = cfg->line_outs; - if (cfg->line_outs == 1) - mangle_smart51(codec); - - err = via_auto_fill_dac_nids(codec); - if (err < 0) - return err; - - if (spec->multiout.num_dacs < 3) { - spec->smart51_nums = 0; - cfg->line_outs = old_line_outs; - } - for (i = 0; i < cfg->line_outs; i++) { - hda_nid_t pin, dac; - pin = cfg->line_out_pins[i]; - dac = spec->multiout.dac_nids[i]; - if (!pin || !dac) - continue; - path = spec->out_path + i; - if (i == HDA_CLFE) { - err = create_ch_ctls(codec, "Center", 1, true, path); - if (err < 0) - return err; - err = create_ch_ctls(codec, "LFE", 2, true, path); - if (err < 0) - return err; - } else { - const char *pfx = chname[i]; - if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && - cfg->line_outs == 1) - pfx = "Speaker"; - err = create_ch_ctls(codec, pfx, 3, true, path); - if (err < 0) - return err; - } - if (path != spec->out_path + i) - copy_path_mixer_ctls(&spec->out_path[i], path); - if (path == spec->out_path && spec->out_mix_path.depth) - copy_path_mixer_ctls(&spec->out_mix_path, path); - } - - idx = get_connection_index(codec, spec->aa_mix_nid, - spec->multiout.dac_nids[0]); - if (idx >= 0) { - /* add control to mixer */ - const char *name; - name = spec->out_mix_path.depth ? - "PCM Loopback Playback Volume" : "PCM Playback Volume"; - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3, - idx, HDA_INPUT)); - if (err < 0) - return err; - name = spec->out_mix_path.depth ? - "PCM Loopback Playback Switch" : "PCM Playback Switch"; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3, - idx, HDA_INPUT)); - if (err < 0) - return err; - } - - cfg->line_outs = old_line_outs; - - return 0; -} - -static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) -{ - struct via_spec *spec = codec->spec; - struct nid_path *path; - bool check_dac; - int i, err; - - if (!pin) - return 0; - - if (!parse_output_path(codec, pin, 0, 0, &spec->hp_indep_path)) { - for (i = HDA_SIDE; i >= HDA_CLFE; i--) { - if (i < spec->multiout.num_dacs && - parse_output_path(codec, pin, - spec->multiout.dac_nids[i], 0, - &spec->hp_indep_path)) { - spec->hp_indep_shared = i; - break; - } - } - } - if (spec->hp_indep_path.depth) { - spec->hp_dac_nid = spec->hp_indep_path.path[0]; - if (!spec->hp_indep_shared) - spec->hp_path = spec->hp_indep_path; - } - /* optionally check front-path w/o AA-mix */ - if (!spec->hp_path.depth) - parse_output_path(codec, pin, - spec->multiout.dac_nids[HDA_FRONT], 0, - &spec->hp_path); - - if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], - 1, &spec->hp_mix_path) && !spec->hp_path.depth) - return 0; - - if (spec->hp_path.depth) { - path = &spec->hp_path; - check_dac = true; - } else { - path = &spec->hp_mix_path; - check_dac = false; - } - err = create_ch_ctls(codec, "Headphone", 3, check_dac, path); - if (err < 0) - return err; - if (check_dac) - copy_path_mixer_ctls(&spec->hp_mix_path, path); - else - copy_path_mixer_ctls(&spec->hp_path, path); - if (spec->hp_indep_path.depth) - copy_path_mixer_ctls(&spec->hp_indep_path, path); - return 0; -} - -static int via_auto_create_speaker_ctls(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - struct nid_path *path; - bool check_dac; - hda_nid_t pin, dac = 0; - int err; - - pin = spec->autocfg.speaker_pins[0]; - if (!spec->autocfg.speaker_outs || !pin) - return 0; - - if (parse_output_path(codec, pin, 0, 0, &spec->speaker_path)) - dac = spec->speaker_path.path[0]; - if (!dac) - parse_output_path(codec, pin, - spec->multiout.dac_nids[HDA_FRONT], 0, - &spec->speaker_path); - if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], - 1, &spec->speaker_mix_path) && !dac) - return 0; - - /* no AA-path for front? */ - if (!spec->out_mix_path.depth && spec->speaker_mix_path.depth) - dac = 0; - - spec->speaker_dac_nid = dac; - spec->multiout.extra_out_nid[0] = dac; - if (dac) { - path = &spec->speaker_path; - check_dac = true; - } else { - path = &spec->speaker_mix_path; - check_dac = false; - } - err = create_ch_ctls(codec, "Speaker", 3, check_dac, path); - if (err < 0) - return err; - if (check_dac) - copy_path_mixer_ctls(&spec->speaker_mix_path, path); - else - copy_path_mixer_ctls(&spec->speaker_path, path); - return 0; -} - -#define via_aamix_ctl_info via_pin_power_ctl_info - -static int via_aamix_ctl_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - ucontrol->value.enumerated.item[0] = spec->aamix_mode; - return 0; -} - -static void update_aamix_paths(struct hda_codec *codec, int do_mix, - struct nid_path *nomix, struct nid_path *mix) -{ - if (do_mix) { - activate_output_path(codec, nomix, false, false); - activate_output_path(codec, mix, true, false); - } else { - activate_output_path(codec, mix, false, false); - activate_output_path(codec, nomix, true, false); - } -} - -static int via_aamix_ctl_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - unsigned int val = ucontrol->value.enumerated.item[0]; - - if (val == spec->aamix_mode) - return 0; - spec->aamix_mode = val; - /* update front path */ - update_aamix_paths(codec, val, &spec->out_path[0], &spec->out_mix_path); - /* update HP path */ - if (!spec->hp_independent_mode) { - update_aamix_paths(codec, val, &spec->hp_path, - &spec->hp_mix_path); - } - /* update speaker path */ - update_aamix_paths(codec, val, &spec->speaker_path, - &spec->speaker_mix_path); - return 1; -} - -static const struct snd_kcontrol_new via_aamix_ctl_enum = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Loopback Mixing", - .info = via_aamix_ctl_info, - .get = via_aamix_ctl_get, - .put = via_aamix_ctl_put, -}; - -static int via_auto_create_loopback_switch(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - - if (!spec->aa_mix_nid) - return 0; /* no loopback switching available */ - if (!(spec->out_mix_path.depth || spec->hp_mix_path.depth || - spec->speaker_path.depth)) - return 0; /* no loopback switching available */ - if (!via_clone_control(spec, &via_aamix_ctl_enum)) - return -ENOMEM; - return 0; -} - -/* look for ADCs */ -static int via_fill_adcs(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - hda_nid_t nid = codec->start_nid; - int i; - - for (i = 0; i < codec->num_nodes; i++, nid++) { - unsigned int wcaps = get_wcaps(codec, nid); - if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) - continue; - if (wcaps & AC_WCAP_DIGITAL) - continue; - if (!(wcaps & AC_WCAP_CONN_LIST)) - continue; - if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids)) - return -ENOMEM; - spec->adc_nids[spec->num_adc_nids++] = nid; - } - return 0; -} - -/* input-src control */ -static int via_mux_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = spec->num_inputs; - if (uinfo->value.enumerated.item >= spec->num_inputs) - uinfo->value.enumerated.item = spec->num_inputs - 1; - strcpy(uinfo->value.enumerated.name, - spec->inputs[uinfo->value.enumerated.item].label); - return 0; -} - -static int via_mux_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - - ucontrol->value.enumerated.item[0] = spec->cur_mux[idx]; - return 0; -} - -static int via_mux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - hda_nid_t mux; - int cur; - - cur = ucontrol->value.enumerated.item[0]; - if (cur < 0 || cur >= spec->num_inputs) - return -EINVAL; - if (spec->cur_mux[idx] == cur) - return 0; - spec->cur_mux[idx] = cur; - if (spec->dyn_adc_switch) { - int adc_idx = spec->inputs[cur].adc_idx; - mux = spec->mux_nids[adc_idx]; - via_dyn_adc_pcm_resetup(codec, cur); - } else { - mux = spec->mux_nids[idx]; - if (snd_BUG_ON(!mux)) - return -EINVAL; - } - - if (mux) { - /* switch to D0 beofre change index */ - update_power_state(codec, mux, AC_PWRST_D0); - snd_hda_codec_write(codec, mux, 0, - AC_VERB_SET_CONNECT_SEL, - spec->inputs[cur].mux_idx); - } - - /* update jack power state */ - set_widgets_power_state(codec); - return 0; -} - -static const struct snd_kcontrol_new via_input_src_ctl = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .info = via_mux_enum_info, - .get = via_mux_enum_get, - .put = via_mux_enum_put, -}; - -static int create_input_src_ctls(struct hda_codec *codec, int count) -{ - struct via_spec *spec = codec->spec; - struct snd_kcontrol_new *knew; - - if (spec->num_inputs <= 1 || !count) - return 0; /* no need for single src */ - - knew = via_clone_control(spec, &via_input_src_ctl); - if (!knew) - return -ENOMEM; - knew->count = count; - return 0; -} - -/* add the powersave loopback-list entry */ -static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx) -{ - struct hda_amp_list *list; - - if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1) - return; - list = spec->loopback_list + spec->num_loopbacks; - list->nid = mix; - list->dir = HDA_INPUT; - list->idx = idx; - spec->num_loopbacks++; - spec->loopback.amplist = spec->loopback_list; -} - -static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src, - hda_nid_t dst) -{ - return snd_hda_get_conn_index(codec, src, dst, 1) >= 0; -} - -/* add the input-route to the given pin */ -static bool add_input_route(struct hda_codec *codec, hda_nid_t pin) -{ - struct via_spec *spec = codec->spec; - int c, idx; - - spec->inputs[spec->num_inputs].adc_idx = -1; - spec->inputs[spec->num_inputs].pin = pin; - for (c = 0; c < spec->num_adc_nids; c++) { - if (spec->mux_nids[c]) { - idx = get_connection_index(codec, spec->mux_nids[c], - pin); - if (idx < 0) - continue; - spec->inputs[spec->num_inputs].mux_idx = idx; - } else { - if (!is_reachable_nid(codec, spec->adc_nids[c], pin)) - continue; - } - spec->inputs[spec->num_inputs].adc_idx = c; - /* Can primary ADC satisfy all inputs? */ - if (!spec->dyn_adc_switch && - spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) { - snd_printd(KERN_INFO - "via: dynamic ADC switching enabled\n"); - spec->dyn_adc_switch = 1; - } - return true; - } - return false; -} - -static int get_mux_nids(struct hda_codec *codec); - -/* parse input-routes; fill ADCs, MUXs and input-src entries */ -static int parse_analog_inputs(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; - int i, err; - - err = via_fill_adcs(codec); - if (err < 0) - return err; - err = get_mux_nids(codec); - if (err < 0) - return err; - - /* fill all input-routes */ - for (i = 0; i < cfg->num_inputs; i++) { - if (add_input_route(codec, cfg->inputs[i].pin)) - spec->inputs[spec->num_inputs++].label = - hda_get_autocfg_input_label(codec, cfg, i); - } - - /* check for internal loopback recording */ - if (spec->aa_mix_nid && - add_input_route(codec, spec->aa_mix_nid)) - spec->inputs[spec->num_inputs++].label = "Stereo Mixer"; - - return 0; -} - -/* create analog-loopback volume/switch controls */ -static int create_loopback_ctls(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; - const char *prev_label = NULL; - int type_idx = 0; - int i, j, err, idx; - - if (!spec->aa_mix_nid) - return 0; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t pin = cfg->inputs[i].pin; - const char *label = hda_get_autocfg_input_label(codec, cfg, i); - - if (prev_label && !strcmp(label, prev_label)) - type_idx++; - else - type_idx = 0; - prev_label = label; - idx = get_connection_index(codec, spec->aa_mix_nid, pin); - if (idx >= 0) { - err = via_new_analog_input(spec, label, type_idx, - idx, spec->aa_mix_nid); - if (err < 0) - return err; - add_loopback_list(spec, spec->aa_mix_nid, idx); - } - - /* remember the label for smart51 control */ - for (j = 0; j < spec->smart51_nums; j++) { - if (spec->smart51_pins[j] == pin) { - spec->smart51_idxs[j] = idx; - spec->smart51_labels[j] = label; - break; - } - } - } - return 0; -} - -/* create mic-boost controls (if present) */ -static int create_mic_boost_ctls(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; - const char *prev_label = NULL; - int type_idx = 0; - int i, err; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t pin = cfg->inputs[i].pin; - unsigned int caps; - const char *label; - char name[32]; - - if (cfg->inputs[i].type != AUTO_PIN_MIC) - continue; - caps = query_amp_caps(codec, pin, HDA_INPUT); - if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS)) - continue; - label = hda_get_autocfg_input_label(codec, cfg, i); - if (prev_label && !strcmp(label, prev_label)) - type_idx++; - else - type_idx = 0; - prev_label = label; - snprintf(name, sizeof(name), "%s Boost Volume", label); - err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx, - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT)); - if (err < 0) - return err; - } - return 0; -} - -/* create capture and input-src controls for multiple streams */ -static int create_multi_adc_ctls(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int i, err; - - /* create capture mixer elements */ - for (i = 0; i < spec->num_adc_nids; i++) { - hda_nid_t adc = spec->adc_nids[i]; - err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Capture Volume", i, - HDA_COMPOSE_AMP_VAL(adc, 3, 0, - HDA_INPUT)); - if (err < 0) - return err; - err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Capture Switch", i, - HDA_COMPOSE_AMP_VAL(adc, 3, 0, - HDA_INPUT)); - if (err < 0) - return err; - } - - /* input-source control */ - for (i = 0; i < spec->num_adc_nids; i++) - if (!spec->mux_nids[i]) - break; - err = create_input_src_ctls(codec, i); - if (err < 0) - return err; - return 0; -} - -/* bind capture volume/switch */ -static struct snd_kcontrol_new via_bind_cap_vol_ctl = - HDA_BIND_VOL("Capture Volume", 0); -static struct snd_kcontrol_new via_bind_cap_sw_ctl = - HDA_BIND_SW("Capture Switch", 0); - -static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret, - struct hda_ctl_ops *ops) -{ - struct hda_bind_ctls *ctl; - int i; - - ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL); - if (!ctl) - return -ENOMEM; - ctl->ops = ops; - for (i = 0; i < spec->num_adc_nids; i++) - ctl->values[i] = - HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT); - *ctl_ret = ctl; - return 0; -} - -/* create capture and input-src controls for dynamic ADC-switch case */ -static int create_dyn_adc_ctls(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - struct snd_kcontrol_new *knew; - int err; - - /* set up the bind capture ctls */ - err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol); - if (err < 0) - return err; - err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw); - if (err < 0) - return err; - - /* create capture mixer elements */ - knew = via_clone_control(spec, &via_bind_cap_vol_ctl); - if (!knew) - return -ENOMEM; - knew->private_value = (long)spec->bind_cap_vol; - - knew = via_clone_control(spec, &via_bind_cap_sw_ctl); - if (!knew) - return -ENOMEM; - knew->private_value = (long)spec->bind_cap_sw; - - /* input-source control */ - err = create_input_src_ctls(codec, 1); - if (err < 0) - return err; - return 0; -} - -/* parse and create capture-related stuff */ -static int via_auto_create_analog_input_ctls(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int err; - - err = parse_analog_inputs(codec); - if (err < 0) - return err; - if (spec->dyn_adc_switch) - err = create_dyn_adc_ctls(codec); - else - err = create_multi_adc_ctls(codec); - if (err < 0) - return err; - err = create_loopback_ctls(codec); - if (err < 0) - return err; - err = create_mic_boost_ctls(codec); - if (err < 0) - return err; - return 0; -} - -static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int def_conf; - unsigned char seqassoc; - - def_conf = snd_hda_codec_get_pincfg(codec, nid); - seqassoc = (unsigned char) get_defcfg_association(def_conf); - seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); - if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE - && (seqassoc == 0xf0 || seqassoc == 0xff)) { - def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); - snd_hda_codec_set_pincfg(codec, nid, def_conf); - } - - return; -} - -static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - - if (spec->codec_type != VT1708) - return 0; - ucontrol->value.integer.value[0] = spec->vt1708_jack_detect; - return 0; -} - -static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - int val; - - if (spec->codec_type != VT1708) - return 0; - val = !!ucontrol->value.integer.value[0]; - if (spec->vt1708_jack_detect == val) - return 0; - spec->vt1708_jack_detect = val; - if (spec->vt1708_jack_detect && - snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") != 1) { - mute_aa_path(codec, 1); - notify_aa_path_ctls(codec); - } - via_hp_automute(codec); - vt1708_update_hp_work(spec); - return 1; -} - -static const struct snd_kcontrol_new vt1708_jack_detect_ctl = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Jack Detect", - .count = 1, - .info = snd_ctl_boolean_mono_info, - .get = vt1708_jack_detect_get, - .put = vt1708_jack_detect_put, -}; - -static void fill_dig_outs(struct hda_codec *codec); -static void fill_dig_in(struct hda_codec *codec); - -static int via_parse_auto_config(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int err; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return -EINVAL; - - err = via_auto_create_multi_out_ctls(codec); - if (err < 0) - return err; - err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); - if (err < 0) - return err; - err = via_auto_create_speaker_ctls(codec); - if (err < 0) - return err; - err = via_auto_create_loopback_switch(codec); - if (err < 0) - return err; - err = via_auto_create_analog_input_ctls(codec); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - fill_dig_outs(codec); - fill_dig_in(codec); - - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; - - - if (spec->hp_dac_nid && spec->hp_mix_path.depth) { - err = via_hp_build(codec); - if (err < 0) - return err; - } - - err = via_smart51_build(codec); - if (err < 0) - return err; - - /* assign slave outs */ - if (spec->slave_dig_outs[0]) - codec->slave_dig_outs = spec->slave_dig_outs; - - return 1; -} - -static void via_auto_init_dig_outs(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - if (spec->multiout.dig_out_nid) - init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT); - if (spec->slave_dig_outs[0]) - init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT); -} - -static void via_auto_init_dig_in(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - if (!spec->dig_in_nid) - return; - snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); -} - -/* initialize the unsolicited events */ -static void via_auto_init_unsol_event(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int ev; - int i; - - if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0])) - snd_hda_jack_detect_enable(codec, cfg->hp_pins[0], - VIA_HP_EVENT | VIA_JACK_EVENT); - - if (cfg->speaker_pins[0]) - ev = VIA_LINE_EVENT; - else - ev = 0; - for (i = 0; i < cfg->line_outs; i++) { - if (cfg->line_out_pins[i] && - is_jack_detectable(codec, cfg->line_out_pins[i])) - snd_hda_jack_detect_enable(codec, cfg->line_out_pins[i], - ev | VIA_JACK_EVENT); - } - - for (i = 0; i < cfg->num_inputs; i++) { - if (is_jack_detectable(codec, cfg->inputs[i].pin)) - snd_hda_jack_detect_enable(codec, cfg->inputs[i].pin, - VIA_JACK_EVENT); - } -} - -static int via_init(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->num_iverbs; i++) - snd_hda_sequence_write(codec, spec->init_verbs[i]); - - /* init power states */ - set_widgets_power_state(codec); - __analog_low_current_mode(codec, true); - - via_auto_init_multi_out(codec); - via_auto_init_hp_out(codec); - via_auto_init_speaker_out(codec); - via_auto_init_analog_input(codec); - via_auto_init_dig_outs(codec); - via_auto_init_dig_in(codec); - - via_auto_init_unsol_event(codec); - - via_hp_automute(codec); - vt1708_update_hp_work(spec); - snd_hda_jack_report_sync(codec); - - return 0; -} - -static void vt1708_update_hp_jack_state(struct work_struct *work) -{ - struct via_spec *spec = container_of(work, struct via_spec, - vt1708_hp_work.work); - if (spec->codec_type != VT1708) - return; - snd_hda_jack_set_dirty_all(spec->codec); - /* if jack state toggled */ - if (spec->vt1708_hp_present - != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) { - spec->vt1708_hp_present ^= 1; - via_hp_automute(spec->codec); - } - if (spec->vt1708_jack_detect) - schedule_delayed_work(&spec->vt1708_hp_work, - msecs_to_jiffies(100)); -} - -static int get_mux_nids(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - hda_nid_t nid, conn[8]; - unsigned int type; - int i, n; - - for (i = 0; i < spec->num_adc_nids; i++) { - nid = spec->adc_nids[i]; - while (nid) { - type = get_wcaps_type(get_wcaps(codec, nid)); - if (type == AC_WID_PIN) - break; - n = snd_hda_get_connections(codec, nid, conn, - ARRAY_SIZE(conn)); - if (n <= 0) - break; - if (n > 1) { - spec->mux_nids[i] = nid; - break; - } - nid = conn[0]; - } - } - return 0; -} - -static int patch_vt1708(struct hda_codec *codec) -{ - struct via_spec *spec; - int err; - - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - - spec->aa_mix_nid = 0x17; - - /* Add HP and CD pin config connect bit re-config action */ - vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); - vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); - - /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) { - via_free(codec); - return err; - } - - /* add jack detect on/off control */ - if (!via_clone_control(spec, &vt1708_jack_detect_ctl)) - return -ENOMEM; - - /* disable 32bit format on VT1708 */ - if (codec->vendor_id == 0x11061708) - spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback; - - spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs; - - codec->patch_ops = via_patch_ops; - - INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state); - return 0; -} - -static int patch_vt1709(struct hda_codec *codec) -{ - struct via_spec *spec; - int err; - - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - - spec->aa_mix_nid = 0x18; - - err = via_parse_auto_config(codec); - if (err < 0) { - via_free(codec); - return err; - } - - codec->patch_ops = via_patch_ops; - - return 0; -} - -static void set_widgets_power_state_vt1708B(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int imux_is_smixer; - unsigned int parm; - int is_8ch = 0; - if ((spec->codec_type != VT1708B_4CH) && - (codec->vendor_id != 0x11064397)) - is_8ch = 1; - - /* SW0 (17h) = stereo mixer */ - imux_is_smixer = - (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) - == ((spec->codec_type == VT1708S) ? 5 : 0)); - /* inputs */ - /* PW 1/2/5 (1ah/1bh/1eh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x1a, &parm); - set_pin_power_state(codec, 0x1b, &parm); - set_pin_power_state(codec, 0x1e, &parm); - if (imux_is_smixer) - parm = AC_PWRST_D0; - /* SW0 (17h), AIW 0/1 (13h/14h) */ - update_power_state(codec, 0x17, parm); - update_power_state(codec, 0x13, parm); - update_power_state(codec, 0x14, parm); - - /* outputs */ - /* PW0 (19h), SW1 (18h), AOW1 (11h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x19, &parm); - if (spec->smart51_enabled) - set_pin_power_state(codec, 0x1b, &parm); - update_power_state(codec, 0x18, parm); - update_power_state(codec, 0x11, parm); - - /* PW6 (22h), SW2 (26h), AOW2 (24h) */ - if (is_8ch) { - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x22, &parm); - if (spec->smart51_enabled) - set_pin_power_state(codec, 0x1a, &parm); - update_power_state(codec, 0x26, parm); - update_power_state(codec, 0x24, parm); - } else if (codec->vendor_id == 0x11064397) { - /* PW7(23h), SW2(27h), AOW2(25h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x23, &parm); - if (spec->smart51_enabled) - set_pin_power_state(codec, 0x1a, &parm); - update_power_state(codec, 0x27, parm); - update_power_state(codec, 0x25, parm); - } - - /* PW 3/4/7 (1ch/1dh/23h) */ - parm = AC_PWRST_D3; - /* force to D0 for internal Speaker */ - set_pin_power_state(codec, 0x1c, &parm); - set_pin_power_state(codec, 0x1d, &parm); - if (is_8ch) - set_pin_power_state(codec, 0x23, &parm); - - /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */ - update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm); - update_power_state(codec, 0x10, parm); - if (is_8ch) { - update_power_state(codec, 0x25, parm); - update_power_state(codec, 0x27, parm); - } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode) - update_power_state(codec, 0x25, parm); -} - -static int patch_vt1708S(struct hda_codec *codec); -static int patch_vt1708B(struct hda_codec *codec) -{ - struct via_spec *spec; - int err; - - if (get_codec_type(codec) == VT1708BCE) - return patch_vt1708S(codec); - - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - - spec->aa_mix_nid = 0x16; - - /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) { - via_free(codec); - return err; - } - - codec->patch_ops = via_patch_ops; - - spec->set_widgets_power_state = set_widgets_power_state_vt1708B; - - return 0; -} - -/* Patch for VT1708S */ -static const struct hda_verb vt1708S_init_verbs[] = { - /* Enable Mic Boost Volume backdoor */ - {0x1, 0xf98, 0x1}, - /* don't bybass mixer */ - {0x1, 0xf88, 0xc0}, - { } -}; - -/* fill out digital output widgets; one for master and one for slave outputs */ -static void fill_dig_outs(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->autocfg.dig_outs; i++) { - hda_nid_t nid; - int conn; - - nid = spec->autocfg.dig_out_pins[i]; - if (!nid) - continue; - conn = snd_hda_get_connections(codec, nid, &nid, 1); - if (conn < 1) - continue; - if (!spec->multiout.dig_out_nid) - spec->multiout.dig_out_nid = nid; - else { - spec->slave_dig_outs[0] = nid; - break; /* at most two dig outs */ - } - } -} - -static void fill_dig_in(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - hda_nid_t dig_nid; - int i, err; - - if (!spec->autocfg.dig_in_pin) - return; - - dig_nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, dig_nid++) { - unsigned int wcaps = get_wcaps(codec, dig_nid); - if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) - continue; - if (!(wcaps & AC_WCAP_DIGITAL)) - continue; - if (!(wcaps & AC_WCAP_CONN_LIST)) - continue; - err = get_connection_index(codec, dig_nid, - spec->autocfg.dig_in_pin); - if (err >= 0) { - spec->dig_in_nid = dig_nid; - break; - } - } -} - -static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, - int offset, int num_steps, int step_size) -{ - snd_hda_override_amp_caps(codec, pin, HDA_INPUT, - (offset << AC_AMPCAP_OFFSET_SHIFT) | - (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) | - (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) | - (0 << AC_AMPCAP_MUTE_SHIFT)); -} - -static int patch_vt1708S(struct hda_codec *codec) -{ - struct via_spec *spec; - int err; - - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - - spec->aa_mix_nid = 0x16; - override_mic_boost(codec, 0x1a, 0, 3, 40); - override_mic_boost(codec, 0x1e, 0, 3, 40); - - /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) { - via_free(codec); - return err; - } - - spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs; - - codec->patch_ops = via_patch_ops; - - /* correct names for VT1708BCE */ - if (get_codec_type(codec) == VT1708BCE) { - kfree(codec->chip_name); - codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL); - snprintf(codec->bus->card->mixername, - sizeof(codec->bus->card->mixername), - "%s %s", codec->vendor_name, codec->chip_name); - } - /* correct names for VT1705 */ - if (codec->vendor_id == 0x11064397) { - kfree(codec->chip_name); - codec->chip_name = kstrdup("VT1705", GFP_KERNEL); - snprintf(codec->bus->card->mixername, - sizeof(codec->bus->card->mixername), - "%s %s", codec->vendor_name, codec->chip_name); - } - spec->set_widgets_power_state = set_widgets_power_state_vt1708B; - return 0; -} - -/* Patch for VT1702 */ - -static const struct hda_verb vt1702_init_verbs[] = { - /* mixer enable */ - {0x1, 0xF88, 0x3}, - /* GPIO 0~2 */ - {0x1, 0xF82, 0x3F}, - { } -}; - -static void set_widgets_power_state_vt1702(struct hda_codec *codec) -{ - int imux_is_smixer = - snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; - unsigned int parm; - /* inputs */ - /* PW 1/2/5 (14h/15h/18h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x14, &parm); - set_pin_power_state(codec, 0x15, &parm); - set_pin_power_state(codec, 0x18, &parm); - if (imux_is_smixer) - parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */ - /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */ - update_power_state(codec, 0x13, parm); - update_power_state(codec, 0x12, parm); - update_power_state(codec, 0x1f, parm); - update_power_state(codec, 0x20, parm); - - /* outputs */ - /* PW 3/4 (16h/17h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x17, &parm); - set_pin_power_state(codec, 0x16, &parm); - /* MW0 (1ah), AOW 0/1 (10h/1dh) */ - update_power_state(codec, 0x1a, imux_is_smixer ? AC_PWRST_D0 : parm); - update_power_state(codec, 0x10, parm); - update_power_state(codec, 0x1d, parm); -} - -static int patch_vt1702(struct hda_codec *codec) -{ - struct via_spec *spec; - int err; - - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - - spec->aa_mix_nid = 0x1a; - - /* limit AA path volume to 0 dB */ - snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, - (0x17 << AC_AMPCAP_OFFSET_SHIFT) | - (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (1 << AC_AMPCAP_MUTE_SHIFT)); - - /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) { - via_free(codec); - return err; - } - - spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs; - - codec->patch_ops = via_patch_ops; - - spec->set_widgets_power_state = set_widgets_power_state_vt1702; - return 0; -} - -/* Patch for VT1718S */ - -static const struct hda_verb vt1718S_init_verbs[] = { - /* Enable MW0 adjust Gain 5 */ - {0x1, 0xfb2, 0x10}, - /* Enable Boost Volume backdoor */ - {0x1, 0xf88, 0x8}, - - { } -}; - -static void set_widgets_power_state_vt1718S(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int imux_is_smixer; - unsigned int parm; - /* MUX6 (1eh) = stereo mixer */ - imux_is_smixer = - snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; - /* inputs */ - /* PW 5/6/7 (29h/2ah/2bh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x29, &parm); - set_pin_power_state(codec, 0x2a, &parm); - set_pin_power_state(codec, 0x2b, &parm); - if (imux_is_smixer) - parm = AC_PWRST_D0; - /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */ - update_power_state(codec, 0x1e, parm); - update_power_state(codec, 0x1f, parm); - update_power_state(codec, 0x10, parm); - update_power_state(codec, 0x11, parm); - - /* outputs */ - /* PW3 (27h), MW2 (1ah), AOW3 (bh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x27, &parm); - update_power_state(codec, 0x1a, parm); - update_power_state(codec, 0xb, parm); - - /* PW2 (26h), AOW2 (ah) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x26, &parm); - if (spec->smart51_enabled) - set_pin_power_state(codec, 0x2b, &parm); - update_power_state(codec, 0xa, parm); - - /* PW0 (24h), AOW0 (8h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x24, &parm); - if (!spec->hp_independent_mode) /* check for redirected HP */ - set_pin_power_state(codec, 0x28, &parm); - update_power_state(codec, 0x8, parm); - /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ - update_power_state(codec, 0x21, imux_is_smixer ? AC_PWRST_D0 : parm); - - /* PW1 (25h), AOW1 (9h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x25, &parm); - if (spec->smart51_enabled) - set_pin_power_state(codec, 0x2a, &parm); - update_power_state(codec, 0x9, parm); - - if (spec->hp_independent_mode) { - /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x28, &parm); - update_power_state(codec, 0x1b, parm); - update_power_state(codec, 0x34, parm); - update_power_state(codec, 0xc, parm); - } -} - -/* Add a connection to the primary DAC from AA-mixer for some codecs - * This isn't listed from the raw info, but the chip has a secret connection. - */ -static int add_secret_dac_path(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int i, nums; - hda_nid_t conn[8]; - hda_nid_t nid; - - if (!spec->aa_mix_nid) - return 0; - nums = snd_hda_get_connections(codec, spec->aa_mix_nid, conn, - ARRAY_SIZE(conn) - 1); - for (i = 0; i < nums; i++) { - if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT) - return 0; - } - - /* find the primary DAC and add to the connection list */ - nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, nid++) { - unsigned int caps = get_wcaps(codec, nid); - if (get_wcaps_type(caps) == AC_WID_AUD_OUT && - !(caps & AC_WCAP_DIGITAL)) { - conn[nums++] = nid; - return snd_hda_override_conn_list(codec, - spec->aa_mix_nid, - nums, conn); - } - } - return 0; -} - - -static int patch_vt1718S(struct hda_codec *codec) -{ - struct via_spec *spec; - int err; - - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - - spec->aa_mix_nid = 0x21; - override_mic_boost(codec, 0x2b, 0, 3, 40); - override_mic_boost(codec, 0x29, 0, 3, 40); - add_secret_dac_path(codec); - - /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) { - via_free(codec); - return err; - } - - spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs; - - codec->patch_ops = via_patch_ops; - - spec->set_widgets_power_state = set_widgets_power_state_vt1718S; - - return 0; -} - -/* Patch for VT1716S */ - -static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} - -static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - int index = 0; - - index = snd_hda_codec_read(codec, 0x26, 0, - AC_VERB_GET_CONNECT_SEL, 0); - if (index != -1) - *ucontrol->value.integer.value = index; - - return 0; -} - -static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - int index = *ucontrol->value.integer.value; - - snd_hda_codec_write(codec, 0x26, 0, - AC_VERB_SET_CONNECT_SEL, index); - spec->dmic_enabled = index; - set_widgets_power_state(codec); - return 1; -} - -static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = { - HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Digital Mic Capture Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x26, - .count = 1, - .info = vt1716s_dmic_info, - .get = vt1716s_dmic_get, - .put = vt1716s_dmic_put, - }, - {} /* end */ -}; - - -/* mono-out mixer elements */ -static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = { - HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct hda_verb vt1716S_init_verbs[] = { - /* Enable Boost Volume backdoor */ - {0x1, 0xf8a, 0x80}, - /* don't bybass mixer */ - {0x1, 0xf88, 0xc0}, - /* Enable mono output */ - {0x1, 0xf90, 0x08}, - { } -}; - -static void set_widgets_power_state_vt1716S(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int imux_is_smixer; - unsigned int parm; - unsigned int mono_out, present; - /* SW0 (17h) = stereo mixer */ - imux_is_smixer = - (snd_hda_codec_read(codec, 0x17, 0, - AC_VERB_GET_CONNECT_SEL, 0x00) == 5); - /* inputs */ - /* PW 1/2/5 (1ah/1bh/1eh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x1a, &parm); - set_pin_power_state(codec, 0x1b, &parm); - set_pin_power_state(codec, 0x1e, &parm); - if (imux_is_smixer) - parm = AC_PWRST_D0; - /* SW0 (17h), AIW0(13h) */ - update_power_state(codec, 0x17, parm); - update_power_state(codec, 0x13, parm); - - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x1e, &parm); - /* PW11 (22h) */ - if (spec->dmic_enabled) - set_pin_power_state(codec, 0x22, &parm); - else - update_power_state(codec, 0x22, AC_PWRST_D3); - - /* SW2(26h), AIW1(14h) */ - update_power_state(codec, 0x26, parm); - update_power_state(codec, 0x14, parm); - - /* outputs */ - /* PW0 (19h), SW1 (18h), AOW1 (11h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x19, &parm); - /* Smart 5.1 PW2(1bh) */ - if (spec->smart51_enabled) - set_pin_power_state(codec, 0x1b, &parm); - update_power_state(codec, 0x18, parm); - update_power_state(codec, 0x11, parm); - - /* PW7 (23h), SW3 (27h), AOW3 (25h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x23, &parm); - /* Smart 5.1 PW1(1ah) */ - if (spec->smart51_enabled) - set_pin_power_state(codec, 0x1a, &parm); - update_power_state(codec, 0x27, parm); - - /* Smart 5.1 PW5(1eh) */ - if (spec->smart51_enabled) - set_pin_power_state(codec, 0x1e, &parm); - update_power_state(codec, 0x25, parm); - - /* Mono out */ - /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/ - present = snd_hda_jack_detect(codec, 0x1c); - - if (present) - mono_out = 0; - else { - present = snd_hda_jack_detect(codec, 0x1d); - if (!spec->hp_independent_mode && present) - mono_out = 0; - else - mono_out = 1; - } - parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3; - update_power_state(codec, 0x28, parm); - update_power_state(codec, 0x29, parm); - update_power_state(codec, 0x2a, parm); - - /* PW 3/4 (1ch/1dh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x1c, &parm); - set_pin_power_state(codec, 0x1d, &parm); - /* HP Independent Mode, power on AOW3 */ - if (spec->hp_independent_mode) - update_power_state(codec, 0x25, parm); - - /* force to D0 for internal Speaker */ - /* MW0 (16h), AOW0 (10h) */ - update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm); - update_power_state(codec, 0x10, mono_out ? AC_PWRST_D0 : parm); -} - -static int patch_vt1716S(struct hda_codec *codec) -{ - struct via_spec *spec; - int err; - - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - - spec->aa_mix_nid = 0x16; - override_mic_boost(codec, 0x1a, 0, 3, 40); - override_mic_boost(codec, 0x1e, 0, 3, 40); - - /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) { - via_free(codec); - return err; - } - - spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs; - - spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer; - spec->num_mixers++; - - spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; - - codec->patch_ops = via_patch_ops; - - spec->set_widgets_power_state = set_widgets_power_state_vt1716S; - return 0; -} - -/* for vt2002P */ - -static const struct hda_verb vt2002P_init_verbs[] = { - /* Class-D speaker related verbs */ - {0x1, 0xfe0, 0x4}, - {0x1, 0xfe9, 0x80}, - {0x1, 0xfe2, 0x22}, - /* Enable Boost Volume backdoor */ - {0x1, 0xfb9, 0x24}, - /* Enable AOW0 to MW9 */ - {0x1, 0xfb8, 0x88}, - { } -}; - -static const struct hda_verb vt1802_init_verbs[] = { - /* Enable Boost Volume backdoor */ - {0x1, 0xfb9, 0x24}, - /* Enable AOW0 to MW9 */ - {0x1, 0xfb8, 0x88}, - { } -}; - -static void set_widgets_power_state_vt2002P(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int imux_is_smixer; - unsigned int parm; - unsigned int present; - /* MUX9 (1eh) = stereo mixer */ - imux_is_smixer = - snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; - /* inputs */ - /* PW 5/6/7 (29h/2ah/2bh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x29, &parm); - set_pin_power_state(codec, 0x2a, &parm); - set_pin_power_state(codec, 0x2b, &parm); - parm = AC_PWRST_D0; - /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */ - update_power_state(codec, 0x1e, parm); - update_power_state(codec, 0x1f, parm); - update_power_state(codec, 0x10, parm); - update_power_state(codec, 0x11, parm); - - /* outputs */ - /* AOW0 (8h)*/ - update_power_state(codec, 0x8, parm); - - if (spec->codec_type == VT1802) { - /* PW4 (28h), MW4 (18h), MUX4(38h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x28, &parm); - update_power_state(codec, 0x18, parm); - update_power_state(codec, 0x38, parm); - } else { - /* PW4 (26h), MW4 (1ch), MUX4(37h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x26, &parm); - update_power_state(codec, 0x1c, parm); - update_power_state(codec, 0x37, parm); - } - - if (spec->codec_type == VT1802) { - /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x25, &parm); - update_power_state(codec, 0x15, parm); - update_power_state(codec, 0x35, parm); - } else { - /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x25, &parm); - update_power_state(codec, 0x19, parm); - update_power_state(codec, 0x35, parm); - } - - if (spec->hp_independent_mode) - update_power_state(codec, 0x9, AC_PWRST_D0); - - /* Class-D */ - /* PW0 (24h), MW0(18h/14h), MUX0(34h) */ - present = snd_hda_jack_detect(codec, 0x25); - - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x24, &parm); - parm = present ? AC_PWRST_D3 : AC_PWRST_D0; - if (spec->codec_type == VT1802) - update_power_state(codec, 0x14, parm); - else - update_power_state(codec, 0x18, parm); - update_power_state(codec, 0x34, parm); - - /* Mono Out */ - present = snd_hda_jack_detect(codec, 0x26); - - parm = present ? AC_PWRST_D3 : AC_PWRST_D0; - if (spec->codec_type == VT1802) { - /* PW15 (33h), MW8(1ch), MUX8(3ch) */ - update_power_state(codec, 0x33, parm); - update_power_state(codec, 0x1c, parm); - update_power_state(codec, 0x3c, parm); - } else { - /* PW15 (31h), MW8(17h), MUX8(3bh) */ - update_power_state(codec, 0x31, parm); - update_power_state(codec, 0x17, parm); - update_power_state(codec, 0x3b, parm); - } - /* MW9 (21h) */ - if (imux_is_smixer || !is_aa_path_mute(codec)) - update_power_state(codec, 0x21, AC_PWRST_D0); - else - update_power_state(codec, 0x21, AC_PWRST_D3); -} - -/* patch for vt2002P */ -static int patch_vt2002P(struct hda_codec *codec) -{ - struct via_spec *spec; - int err; - - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - - spec->aa_mix_nid = 0x21; - override_mic_boost(codec, 0x2b, 0, 3, 40); - override_mic_boost(codec, 0x29, 0, 3, 40); - add_secret_dac_path(codec); - - /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) { - via_free(codec); - return err; - } - - if (spec->codec_type == VT1802) - spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs; - else - spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs; - - codec->patch_ops = via_patch_ops; - - spec->set_widgets_power_state = set_widgets_power_state_vt2002P; - return 0; -} - -/* for vt1812 */ - -static const struct hda_verb vt1812_init_verbs[] = { - /* Enable Boost Volume backdoor */ - {0x1, 0xfb9, 0x24}, - /* Enable AOW0 to MW9 */ - {0x1, 0xfb8, 0xa8}, - { } -}; - -static void set_widgets_power_state_vt1812(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - unsigned int parm; - unsigned int present; - /* inputs */ - /* PW 5/6/7 (29h/2ah/2bh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x29, &parm); - set_pin_power_state(codec, 0x2a, &parm); - set_pin_power_state(codec, 0x2b, &parm); - parm = AC_PWRST_D0; - /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ - update_power_state(codec, 0x1e, parm); - update_power_state(codec, 0x1f, parm); - update_power_state(codec, 0x10, parm); - update_power_state(codec, 0x11, parm); - - /* outputs */ - /* AOW0 (8h)*/ - update_power_state(codec, 0x8, AC_PWRST_D0); - - /* PW4 (28h), MW4 (18h), MUX4(38h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x28, &parm); - update_power_state(codec, 0x18, parm); - update_power_state(codec, 0x38, parm); - - /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x25, &parm); - update_power_state(codec, 0x15, parm); - update_power_state(codec, 0x35, parm); - if (spec->hp_independent_mode) - update_power_state(codec, 0x9, AC_PWRST_D0); - - /* Internal Speaker */ - /* PW0 (24h), MW0(14h), MUX0(34h) */ - present = snd_hda_jack_detect(codec, 0x25); - - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x24, &parm); - if (present) { - update_power_state(codec, 0x14, AC_PWRST_D3); - update_power_state(codec, 0x34, AC_PWRST_D3); - } else { - update_power_state(codec, 0x14, AC_PWRST_D0); - update_power_state(codec, 0x34, AC_PWRST_D0); - } - - - /* Mono Out */ - /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */ - present = snd_hda_jack_detect(codec, 0x28); - - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x31, &parm); - if (present) { - update_power_state(codec, 0x1c, AC_PWRST_D3); - update_power_state(codec, 0x3c, AC_PWRST_D3); - update_power_state(codec, 0x3e, AC_PWRST_D3); - } else { - update_power_state(codec, 0x1c, AC_PWRST_D0); - update_power_state(codec, 0x3c, AC_PWRST_D0); - update_power_state(codec, 0x3e, AC_PWRST_D0); - } - - /* PW15 (33h), MW15 (1dh), MUX15(3dh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x33, &parm); - update_power_state(codec, 0x1d, parm); - update_power_state(codec, 0x3d, parm); - -} - -/* patch for vt1812 */ -static int patch_vt1812(struct hda_codec *codec) -{ - struct via_spec *spec; - int err; - - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - - spec->aa_mix_nid = 0x21; - override_mic_boost(codec, 0x2b, 0, 3, 40); - override_mic_boost(codec, 0x29, 0, 3, 40); - add_secret_dac_path(codec); - - /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) { - via_free(codec); - return err; - } - - spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs; - - codec->patch_ops = via_patch_ops; - - spec->set_widgets_power_state = set_widgets_power_state_vt1812; - return 0; -} - -/* - * patch entries - */ -static const 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}, - { .id = 0x1106e711, .name = "VT1709 10-Ch", - .patch = patch_vt1709}, - { .id = 0x1106e712, .name = "VT1709 10-Ch", - .patch = patch_vt1709}, - { .id = 0x1106e713, .name = "VT1709 10-Ch", - .patch = patch_vt1709}, - { .id = 0x1106e714, .name = "VT1709 6-Ch", - .patch = patch_vt1709}, - { .id = 0x1106e715, .name = "VT1709 6-Ch", - .patch = patch_vt1709}, - { .id = 0x1106e716, .name = "VT1709 6-Ch", - .patch = patch_vt1709}, - { .id = 0x1106e717, .name = "VT1709 6-Ch", - .patch = patch_vt1709}, - { .id = 0x1106e720, .name = "VT1708B 8-Ch", - .patch = patch_vt1708B}, - { .id = 0x1106e721, .name = "VT1708B 8-Ch", - .patch = patch_vt1708B}, - { .id = 0x1106e722, .name = "VT1708B 8-Ch", - .patch = patch_vt1708B}, - { .id = 0x1106e723, .name = "VT1708B 8-Ch", - .patch = patch_vt1708B}, - { .id = 0x1106e724, .name = "VT1708B 4-Ch", - .patch = patch_vt1708B}, - { .id = 0x1106e725, .name = "VT1708B 4-Ch", - .patch = patch_vt1708B}, - { .id = 0x1106e726, .name = "VT1708B 4-Ch", - .patch = patch_vt1708B}, - { .id = 0x1106e727, .name = "VT1708B 4-Ch", - .patch = patch_vt1708B}, - { .id = 0x11060397, .name = "VT1708S", - .patch = patch_vt1708S}, - { .id = 0x11061397, .name = "VT1708S", - .patch = patch_vt1708S}, - { .id = 0x11062397, .name = "VT1708S", - .patch = patch_vt1708S}, - { .id = 0x11063397, .name = "VT1708S", - .patch = patch_vt1708S}, - { .id = 0x11064397, .name = "VT1705", - .patch = patch_vt1708S}, - { .id = 0x11065397, .name = "VT1708S", - .patch = patch_vt1708S}, - { .id = 0x11066397, .name = "VT1708S", - .patch = patch_vt1708S}, - { .id = 0x11067397, .name = "VT1708S", - .patch = patch_vt1708S}, - { .id = 0x11060398, .name = "VT1702", - .patch = patch_vt1702}, - { .id = 0x11061398, .name = "VT1702", - .patch = patch_vt1702}, - { .id = 0x11062398, .name = "VT1702", - .patch = patch_vt1702}, - { .id = 0x11063398, .name = "VT1702", - .patch = patch_vt1702}, - { .id = 0x11064398, .name = "VT1702", - .patch = patch_vt1702}, - { .id = 0x11065398, .name = "VT1702", - .patch = patch_vt1702}, - { .id = 0x11066398, .name = "VT1702", - .patch = patch_vt1702}, - { .id = 0x11067398, .name = "VT1702", - .patch = patch_vt1702}, - { .id = 0x11060428, .name = "VT1718S", - .patch = patch_vt1718S}, - { .id = 0x11064428, .name = "VT1718S", - .patch = patch_vt1718S}, - { .id = 0x11060441, .name = "VT2020", - .patch = patch_vt1718S}, - { .id = 0x11064441, .name = "VT1828S", - .patch = patch_vt1718S}, - { .id = 0x11060433, .name = "VT1716S", - .patch = patch_vt1716S}, - { .id = 0x1106a721, .name = "VT1716S", - .patch = patch_vt1716S}, - { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P}, - { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P}, - { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812}, - { .id = 0x11060440, .name = "VT1818S", - .patch = patch_vt1708S}, - { .id = 0x11060446, .name = "VT1802", - .patch = patch_vt2002P}, - { .id = 0x11068446, .name = "VT1802", - .patch = patch_vt2002P}, - {} /* 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) |