Login
[x]
Log in using an account from:
Fedora Account System
Red Hat Associate
Red Hat Customer
Or login using a Red Hat Bugzilla account
Forgot Password
Login:
Hide Forgot
Create an Account
Red Hat Bugzilla – Attachment 310265 Details for
Bug 452846
FEAT: RHEL 4.8 HDA ALSA driver update from mainstream
[?]
New
Simple Search
Advanced Search
My Links
Browse
Requests
Reports
Current State
Search
Tabular reports
Graphical reports
Duplicates
Other Reports
User Changes
Plotly Reports
Bug Status
Bug Severity
Non-Defaults
|
Product Dashboard
Help
Page Help!
Bug Writing Guidelines
What's new
Browser Support Policy
5.0.4.rh83 Release notes
FAQ
Guides index
User guide
Web Services
Contact
Legal
This site requires JavaScript to be enabled to function correctly, please enable it.
[patch]
update from mainstream for RHEL 4.7
0001--RHEL-4.7-PATCH-ALSA-HDA-driver-update-from-mains.patch (text/plain), 1.16 MB, created by
Jaroslav Kysela
on 2008-06-25 13:38:42 UTC
(
hide
)
Description:
update from mainstream for RHEL 4.7
Filename:
MIME Type:
Creator:
Jaroslav Kysela
Created:
2008-06-25 13:38:42 UTC
Size:
1.16 MB
patch
obsolete
>From b3538a247cdc65bf7be8bb213022658abd3c3b96 Mon Sep 17 00:00:00 2001 >From: Jaroslav Kysela <perex@perex.cz> >Date: Tue, 24 Jun 2008 14:50:20 +0200 >Subject: [PATCH] [RHEL 4.7 PATCH] ALSA - HDA driver update from mainstream 2008-06-11 > >Bugzilla >======== >BZ#452846 >https://bugzilla.redhat.com/show_bug.cgi?id=452846 > >Description >=========== >This patch contains backported mainstream ALSA code for HDA driver. >It enables support for new hardware (integrated audio on motherboards >and notebooks). > >Test Status >=========== >Untested. >--- > include/sound/asound.h | 37 +- > include/sound/compat.h | 21 + > include/sound/control.h | 36 +- > include/sound/core.h | 27 +- > include/sound/hda_hwdep.h | 44 + > include/sound/hwdep.h | 10 +- > include/sound/pcm.h | 33 +- > redhat/configs/config-generic | 1 + > sound/core/Kconfig | 2 + > sound/core/Makefile | 1 + > sound/core/control.c | 27 + > sound/core/ioctl32/ioctl32.c | 4 +- > sound/core/ioctl32/ioctl32.h | 14 + > sound/core/ioctl32/pcm32.c | 8 +- > sound/core/vmaster.c | 352 + > sound/pci/Kconfig | 18 + > sound/pci/Makefile | 2 +- > sound/pci/hda/Makefile | 18 + > sound/pci/hda/hda_codec.c | 3092 +++++++++ > sound/pci/hda/hda_codec.h | 763 +++ > sound/pci/hda/hda_generic.c | 1099 +++ > sound/pci/hda/hda_hwdep.c | 122 + > sound/pci/hda/hda_intel.c | 2271 +++++++ > sound/pci/hda/hda_local.h | 414 ++ > sound/pci/hda/hda_patch.h | 20 + > sound/pci/hda/hda_proc.c | 663 ++ > sound/pci/hda/patch_analog.c | 4221 ++++++++++++ > sound/pci/hda/patch_atihdmi.c | 171 + > sound/pci/hda/patch_cmedia.c | 744 ++ > sound/pci/hda/patch_conexant.c | 1791 +++++ > sound/pci/hda/patch_realtek.c |14665 ++++++++++++++++++++++++++++++++++++++++ > sound/pci/hda/patch_si3054.c | 304 + > sound/pci/hda/patch_sigmatel.c | 4323 ++++++++++++ > sound/pci/hda/patch_via.c | 2027 ++++++ > 34 files changed, 37287 insertions(+), 58 deletions(-) > create mode 100644 include/sound/compat.h > create mode 100644 include/sound/hda_hwdep.h > create mode 100644 sound/core/vmaster.c > create mode 100644 sound/pci/hda/Makefile > create mode 100644 sound/pci/hda/hda_codec.c > create mode 100644 sound/pci/hda/hda_codec.h > create mode 100644 sound/pci/hda/hda_generic.c > create mode 100644 sound/pci/hda/hda_hwdep.c > create mode 100644 sound/pci/hda/hda_intel.c > create mode 100644 sound/pci/hda/hda_local.h > create mode 100644 sound/pci/hda/hda_patch.h > create mode 100644 sound/pci/hda/hda_proc.c > create mode 100644 sound/pci/hda/patch_analog.c > create mode 100644 sound/pci/hda/patch_atihdmi.c > create mode 100644 sound/pci/hda/patch_cmedia.c > create mode 100644 sound/pci/hda/patch_conexant.c > create mode 100644 sound/pci/hda/patch_realtek.c > create mode 100644 sound/pci/hda/patch_si3054.c > create mode 100644 sound/pci/hda/patch_sigmatel.c > create mode 100644 sound/pci/hda/patch_via.c > >diff --git a/include/sound/asound.h b/include/sound/asound.h >index 3d6d12c..bc133e4 100644 >--- a/include/sound/asound.h >+++ b/include/sound/asound.h >@@ -114,9 +114,10 @@ enum sndrv_hwdep_iface { > SNDRV_HWDEP_IFACE_USX2Y_PCM, /* Tascam US122, US224 & US428 rawusb pcm */ > SNDRV_HWDEP_IFACE_PCXHR, /* Digigram PCXHR */ > SNDRV_HWDEP_IFACE_SB_RC, /* SB Extigy/Audigy2NX remote control */ >+ SNDRV_HWDEP_IFACE_HDA, /* HD-audio */ > > /* Don't forget to change the following: */ >- SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_EMUX_WAVETABLE, >+ SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_HDA, > }; > > struct sndrv_hwdep_info { >@@ -361,7 +362,7 @@ struct sndrv_mask { > u_int32_t bits[(SNDRV_MASK_MAX+31)/32]; > }; > >-struct sndrv_pcm_hw_params { >+struct snd_pcm_hw_params { > unsigned int flags; > struct sndrv_mask masks[SNDRV_PCM_HW_PARAM_LAST_MASK - > SNDRV_PCM_HW_PARAM_FIRST_MASK + 1]; >@@ -465,8 +466,8 @@ enum { > SNDRV_PCM_IOCTL_PVERSION = _IOR('A', 0x00, int), > SNDRV_PCM_IOCTL_INFO = _IOR('A', 0x01, struct sndrv_pcm_info), > SNDRV_PCM_IOCTL_TSTAMP = _IOW('A', 0x02, int), >- SNDRV_PCM_IOCTL_HW_REFINE = _IOWR('A', 0x10, struct sndrv_pcm_hw_params), >- SNDRV_PCM_IOCTL_HW_PARAMS = _IOWR('A', 0x11, struct sndrv_pcm_hw_params), >+ SNDRV_PCM_IOCTL_HW_REFINE = _IOWR('A', 0x10, struct snd_pcm_hw_params), >+ SNDRV_PCM_IOCTL_HW_PARAMS = _IOWR('A', 0x11, struct snd_pcm_hw_params), > SNDRV_PCM_IOCTL_HW_FREE = _IO('A', 0x12), > SNDRV_PCM_IOCTL_SW_PARAMS = _IOWR('A', 0x13, struct sndrv_pcm_sw_params), > SNDRV_PCM_IOCTL_STATUS = _IOR('A', 0x20, struct sndrv_pcm_status), >@@ -772,7 +773,7 @@ enum sndrv_ctl_elem_iface { > #define SNDRV_CTL_POWER_D3hot (SNDRV_CTL_POWER_D3|0x0000) /* Off, with power */ > #define SNDRV_CTL_POWER_D3cold (SNDRV_CTL_POWER_D3|0x0001) /* Off, without power */ > >-struct sndrv_ctl_elem_id { >+struct snd_ctl_elem_id { > unsigned int numid; /* numeric identifier, zero = invalid */ > enum sndrv_ctl_elem_iface iface; /* interface identifier */ > unsigned int device; /* device/client number */ >@@ -790,8 +791,8 @@ struct sndrv_ctl_elem_list { > unsigned char reserved[50]; > }; > >-struct sndrv_ctl_elem_info { >- struct sndrv_ctl_elem_id id; /* W: element ID */ >+struct snd_ctl_elem_info { >+ struct snd_ctl_elem_id id; /* W: element ID */ > enum sndrv_ctl_elem_type type; /* R: value type - SNDRV_CTL_ELEM_TYPE_* */ > unsigned int access; /* R: value access (bitmask) - SNDRV_CTL_ELEM_ACCESS_* */ > unsigned int count; /* count of values */ >@@ -821,8 +822,8 @@ struct sndrv_ctl_elem_info { > unsigned char reserved[64-4*sizeof(unsigned short)]; > }; > >-struct sndrv_ctl_elem_value { >- struct sndrv_ctl_elem_id id; /* W: element ID */ >+struct snd_ctl_elem_value { >+ struct snd_ctl_elem_id id; /* W: element ID */ > unsigned int indirect: 1; /* W: use indirect pointer (xxx_ptr member) */ > union { > union { >@@ -851,15 +852,15 @@ enum { > SNDRV_CTL_IOCTL_PVERSION = _IOR('U', 0x00, int), > SNDRV_CTL_IOCTL_CARD_INFO = _IOR('U', 0x01, struct sndrv_ctl_card_info), > SNDRV_CTL_IOCTL_ELEM_LIST = _IOWR('U', 0x10, struct sndrv_ctl_elem_list), >- SNDRV_CTL_IOCTL_ELEM_INFO = _IOWR('U', 0x11, struct sndrv_ctl_elem_info), >- SNDRV_CTL_IOCTL_ELEM_READ = _IOWR('U', 0x12, struct sndrv_ctl_elem_value), >- SNDRV_CTL_IOCTL_ELEM_WRITE = _IOWR('U', 0x13, struct sndrv_ctl_elem_value), >- SNDRV_CTL_IOCTL_ELEM_LOCK = _IOW('U', 0x14, struct sndrv_ctl_elem_id), >- SNDRV_CTL_IOCTL_ELEM_UNLOCK = _IOW('U', 0x15, struct sndrv_ctl_elem_id), >+ SNDRV_CTL_IOCTL_ELEM_INFO = _IOWR('U', 0x11, struct snd_ctl_elem_info), >+ SNDRV_CTL_IOCTL_ELEM_READ = _IOWR('U', 0x12, struct snd_ctl_elem_value), >+ SNDRV_CTL_IOCTL_ELEM_WRITE = _IOWR('U', 0x13, struct snd_ctl_elem_value), >+ SNDRV_CTL_IOCTL_ELEM_LOCK = _IOW('U', 0x14, struct snd_ctl_elem_id), >+ SNDRV_CTL_IOCTL_ELEM_UNLOCK = _IOW('U', 0x15, struct snd_ctl_elem_id), > SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS = _IOWR('U', 0x16, int), >- SNDRV_CTL_IOCTL_ELEM_ADD = _IOWR('U', 0x17, struct sndrv_ctl_elem_info), >- SNDRV_CTL_IOCTL_ELEM_REPLACE = _IOWR('U', 0x18, struct sndrv_ctl_elem_info), >- SNDRV_CTL_IOCTL_ELEM_REMOVE = _IOWR('U', 0x19, struct sndrv_ctl_elem_id), >+ SNDRV_CTL_IOCTL_ELEM_ADD = _IOWR('U', 0x17, struct snd_ctl_elem_info), >+ SNDRV_CTL_IOCTL_ELEM_REPLACE = _IOWR('U', 0x18, struct snd_ctl_elem_info), >+ SNDRV_CTL_IOCTL_ELEM_REMOVE = _IOWR('U', 0x19, struct snd_ctl_elem_id), > SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE = _IOWR('U', 0x20, int), > SNDRV_CTL_IOCTL_HWDEP_INFO = _IOR('U', 0x21, struct sndrv_hwdep_info), > SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE = _IOR('U', 0x30, int), >@@ -891,7 +892,7 @@ struct sndrv_ctl_event { > union { > struct { > unsigned int mask; >- struct sndrv_ctl_elem_id id; >+ struct snd_ctl_elem_id id; > } elem; > unsigned char data8[60]; > } data; >diff --git a/include/sound/compat.h b/include/sound/compat.h >new file mode 100644 >index 0000000..4d03fe7 >--- /dev/null >+++ b/include/sound/compat.h >@@ -0,0 +1,21 @@ >+#ifdef CONFIG_PCI >+#include <linux/pci.h> >+#define PCI_D0 0 >+#define PCI_D3hot 3 >+#define pci_intx(pci,x) do { } while (0) >+#define pci_choose_state(pci,state) ((state) ? PCI_D3hot : PCI_D0) >+#endif >+ >+#define IRQF_SHARED SA_SHIRQ >+ >+#define module_param_array1(name, type, nump, perm) \ >+ static unsigned int boot_devs_##name; \ >+ module_param_array_named(name, name, type, boot_devs_##name, perm) >+ >+#include <linux/pm.h> >+#ifndef PMSG_FREEZE >+typedef u32 __bitwise pm_message_t; >+#define PMSG_FREEZE 3 >+#define PMSG_SUSPEND 3 >+#define PMSG_ON 0 >+#endif >diff --git a/include/sound/control.h b/include/sound/control.h >index e99a02c..171fb5a 100644 >--- a/include/sound/control.h >+++ b/include/sound/control.h >@@ -28,20 +28,27 @@ typedef struct sndrv_aes_iec958 snd_aes_iec958_t; > typedef struct sndrv_ctl_card_info snd_ctl_card_info_t; > typedef enum sndrv_ctl_elem_type snd_ctl_elem_type_t; > typedef enum sndrv_ctl_elem_iface snd_ctl_elem_iface_t; >-typedef struct sndrv_ctl_elem_id snd_ctl_elem_id_t; >+typedef struct snd_ctl_elem_id snd_ctl_elem_id_t; > typedef struct sndrv_ctl_elem_list snd_ctl_elem_list_t; >-typedef struct sndrv_ctl_elem_info snd_ctl_elem_info_t; >-typedef struct sndrv_ctl_elem_value snd_ctl_elem_value_t; >+typedef struct snd_ctl_elem_info snd_ctl_elem_info_t; >+typedef struct snd_ctl_elem_value snd_ctl_elem_value_t; > typedef enum sndrv_ctl_event_type snd_ctl_event_type_t; > typedef struct sndrv_ctl_event snd_ctl_event_t; > >+#define sndrv_ctl_elem_info snd_ctl_elem_info >+#define sndrv_ctl_elem_value snd_ctl_elem_value >+ > #define snd_kcontrol_chip(kcontrol) ((kcontrol)->private_data) > > typedef int (snd_kcontrol_info_t) (snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo); > typedef int (snd_kcontrol_get_t) (snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); > typedef int (snd_kcontrol_put_t) (snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); >+typedef int (snd_kcontrol_tlv_rw_t) (snd_kcontrol_t * kcontrol, >+ int op_flag, >+ unsigned int size, >+ unsigned int __user *tlv); > >-typedef struct _snd_kcontrol_new { >+typedef struct snd_kcontrol_new { > snd_ctl_elem_iface_t iface; /* interface identifier */ > unsigned int device; /* device/client number */ > unsigned int subdevice; /* subdevice (substream) number */ >@@ -52,6 +59,10 @@ typedef struct _snd_kcontrol_new { > snd_kcontrol_info_t *info; > snd_kcontrol_get_t *get; > snd_kcontrol_put_t *put; >+ union { >+ snd_kcontrol_tlv_rw_t *c; >+ const unsigned int *p; >+ } tlv; > unsigned long private_value; > } snd_kcontrol_new_t; > >@@ -61,7 +72,7 @@ typedef struct _snd_kcontrol_volatile { > unsigned int access; /* access rights */ > } snd_kcontrol_volatile_t; > >-struct _snd_kcontrol { >+struct snd_kcontrol { > struct list_head list; /* list of controls */ > snd_ctl_elem_id_t id; > unsigned int count; /* count of same elements */ >@@ -154,4 +165,19 @@ static inline snd_ctl_elem_id_t *snd_ctl_build_ioff(snd_ctl_elem_id_t *dst_id, > return dst_id; > } > >+/* >+ * Frequently used control callbacks >+ */ >+int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol, >+ struct snd_ctl_elem_info *uinfo); >+int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol, >+ struct snd_ctl_elem_info *uinfo); >+ >+/* >+ * virtual master control >+ */ >+struct snd_kcontrol *snd_ctl_make_virtual_master(char *name, >+ const unsigned int *tlv); >+int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave); >+ > #endif /* __SOUND_CONTROL_H */ >diff --git a/include/sound/core.h b/include/sound/core.h >index 59d6f94..1db4481 100644 >--- a/include/sound/core.h >+++ b/include/sound/core.h >@@ -26,6 +26,8 @@ > #include <asm/semaphore.h> /* struct semaphore */ > #include <linux/rwsem.h> /* struct rw_semaphore */ > #include <linux/workqueue.h> /* struct workqueue_struct */ >+#include <linux/mutex.h> >+#include "compat.h" > > /* Typedef's */ > typedef struct timespec snd_timestamp_t; >@@ -43,6 +45,12 @@ struct pci_dev; > struct sbus_dev; > #endif > >+#define IRQF_SHARED SA_SHIRQ >+ >+#define module_param_array1(name, type, nump, perm) \ >+ static unsigned int boot_devs_##name; \ >+ module_param_array_named(name, name, type, boot_devs_##name, perm) >+ > /* device allocation stuff */ > > #define SNDRV_DEV_TYPE_RANGE_SIZE 0x1000 >@@ -57,6 +65,7 @@ typedef enum { > SNDRV_DEV_SEQUENCER, > SNDRV_DEV_HWDEP, > SNDRV_DEV_INFO, >+ SNDRV_DEV_BUS, > SNDRV_DEV_LOWLEVEL = (2*SNDRV_DEV_TYPE_RANGE_SIZE) > } snd_device_type_t; > >@@ -72,22 +81,22 @@ typedef enum { > SNDRV_DEV_CMD_POST = 2 > } snd_device_cmd_t; > >-typedef struct _snd_card snd_card_t; >-typedef struct _snd_device snd_device_t; >+typedef struct snd_card snd_card_t; >+typedef struct snd_device snd_device_t; > > typedef int (snd_dev_free_t)(snd_device_t *device); > typedef int (snd_dev_register_t)(snd_device_t *device); > typedef int (snd_dev_disconnect_t)(snd_device_t *device); > typedef int (snd_dev_unregister_t)(snd_device_t *device); > >-typedef struct { >+typedef struct snd_device_ops { > snd_dev_free_t *dev_free; > snd_dev_register_t *dev_register; > snd_dev_disconnect_t *dev_disconnect; > snd_dev_unregister_t *dev_unregister; > } snd_device_ops_t; > >-struct _snd_device { >+struct snd_device { > struct list_head list; /* list of registered devices */ > snd_card_t *card; /* card which holds this device */ > snd_device_state_t state; /* state of the device */ >@@ -101,16 +110,16 @@ struct _snd_device { > /* various typedefs */ > > typedef struct snd_info_entry snd_info_entry_t; >-typedef struct _snd_pcm snd_pcm_t; >+typedef struct snd_pcm snd_pcm_t; > typedef struct _snd_pcm_str snd_pcm_str_t; >-typedef struct _snd_pcm_substream snd_pcm_substream_t; >+typedef struct snd_pcm_substream snd_pcm_substream_t; > typedef struct _snd_mixer snd_kmixer_t; > typedef struct _snd_rawmidi snd_rawmidi_t; > typedef struct _snd_ctl_file snd_ctl_file_t; >-typedef struct _snd_kcontrol snd_kcontrol_t; >+typedef struct snd_kcontrol snd_kcontrol_t; > typedef struct _snd_timer snd_timer_t; > typedef struct _snd_timer_instance snd_timer_instance_t; >-typedef struct _snd_hwdep snd_hwdep_t; >+typedef struct snd_hwdep snd_hwdep_t; > #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) > typedef struct _snd_oss_mixer snd_mixer_oss_t; > #endif >@@ -126,7 +135,7 @@ struct snd_shutdown_f_ops; /* define it later */ > > /* main structure for soundcard */ > >-struct _snd_card { >+struct snd_card { > int number; /* number of soundcard (index to snd_cards) */ > > char id[16]; /* id string of this card */ >diff --git a/include/sound/hda_hwdep.h b/include/sound/hda_hwdep.h >new file mode 100644 >index 0000000..1c0034e >--- /dev/null >+++ b/include/sound/hda_hwdep.h >@@ -0,0 +1,44 @@ >+/* >+ * 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 >+ */ >+ >+#ifndef __SOUND_HDA_HWDEP_H >+#define __SOUND_HDA_HWDEP_H >+ >+#define HDA_HWDEP_VERSION ((1 << 16) | (0 << 8) | (0 << 0)) /* 1.0.0 */ >+ >+/* verb */ >+#define HDA_REG_NID_SHIFT 24 >+#define HDA_REG_VERB_SHIFT 8 >+#define HDA_REG_VAL_SHIFT 0 >+#define HDA_VERB(nid,verb,param) ((nid)<<24 | (verb)<<8 | (param)) >+ >+struct hda_verb_ioctl { >+ u32 verb; /* HDA_VERB() */ >+ u32 res; /* response */ >+}; >+ >+/* >+ * ioctls >+ */ >+#define HDA_IOCTL_PVERSION _IOR('H', 0x10, int) >+#define HDA_IOCTL_VERB_WRITE _IOWR('H', 0x11, struct hda_verb_ioctl) >+#define HDA_IOCTL_GET_WCAP _IOWR('H', 0x12, struct hda_verb_ioctl) >+ >+#endif >diff --git a/include/sound/hwdep.h b/include/sound/hwdep.h >index 4a4cc01..1409af1 100644 >--- a/include/sound/hwdep.h >+++ b/include/sound/hwdep.h >@@ -30,12 +30,14 @@ typedef struct sndrv_hwdep_info snd_hwdep_info_t; > typedef struct sndrv_hwdep_dsp_status snd_hwdep_dsp_status_t; > typedef struct sndrv_hwdep_dsp_image snd_hwdep_dsp_image_t; > >+struct snd_hwdep; >+ > typedef struct _snd_hwdep_ops { > long long (*llseek) (snd_hwdep_t *hw, struct file * file, long long offset, int orig); > long (*read) (snd_hwdep_t * hw, char __user *buf, long count, loff_t *offset); > long (*write) (snd_hwdep_t * hw, const char __user *buf, long count, loff_t *offset); >- int (*open) (snd_hwdep_t * hw, struct file * file); >- int (*release) (snd_hwdep_t * hw, struct file * file); >+ int (*open) (struct snd_hwdep * hw, struct file * file); >+ int (*release) (struct snd_hwdep * hw, struct file * file); > unsigned int (*poll) (snd_hwdep_t * hw, struct file * file, poll_table * wait); > int (*ioctl) (snd_hwdep_t * hw, struct file * file, unsigned int cmd, unsigned long arg); > int (*mmap) (snd_hwdep_t * hw, struct file * file, struct vm_area_struct * vma); >@@ -43,7 +45,7 @@ typedef struct _snd_hwdep_ops { > int (*dsp_load) (snd_hwdep_t * hw, snd_hwdep_dsp_image_t * image); > } snd_hwdep_ops_t; > >-struct _snd_hwdep { >+struct snd_hwdep { > snd_card_t *card; > int device; > char id[32]; >@@ -67,6 +69,6 @@ struct _snd_hwdep { > unsigned int exclusive: 1; > }; > >-extern int snd_hwdep_new(snd_card_t * card, char *id, int device, snd_hwdep_t ** rhwdep); >+extern int snd_hwdep_new(struct snd_card * card, char *id, int device, struct snd_hwdep ** rhwdep); > > #endif /* __SOUND_HWDEP_H */ >diff --git a/include/sound/pcm.h b/include/sound/pcm.h >index a3d57cd..30aa4a8 100644 >--- a/include/sound/pcm.h >+++ b/include/sound/pcm.h >@@ -40,7 +40,7 @@ typedef enum sndrv_pcm_state snd_pcm_state_t; > typedef union sndrv_pcm_sync_id snd_pcm_sync_id_t; > typedef struct sndrv_pcm_info snd_pcm_info_t; > typedef enum sndrv_pcm_hw_param snd_pcm_hw_param_t; >-typedef struct sndrv_pcm_hw_params snd_pcm_hw_params_t; >+typedef struct snd_pcm_hw_params snd_pcm_hw_params_t; > typedef enum sndrv_pcm_start snd_pcm_start_t; > typedef enum sndrv_pcm_xrun snd_pcm_xrun_t; > typedef enum sndrv_pcm_tstamp snd_pcm_tstamp_t; >@@ -56,7 +56,7 @@ typedef struct snd_sg_buf snd_pcm_sgbuf_t; > #define snd_pcm_chip(pcm) ((pcm)->private_data) > > typedef struct _snd_pcm_file snd_pcm_file_t; >-typedef struct _snd_pcm_runtime snd_pcm_runtime_t; >+typedef struct snd_pcm_runtime snd_pcm_runtime_t; > > #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) > #include "pcm_oss.h" >@@ -66,7 +66,7 @@ typedef struct _snd_pcm_runtime snd_pcm_runtime_t; > * Hardware (lowlevel) section > */ > >-typedef struct _snd_pcm_hardware { >+typedef struct snd_pcm_hardware { > unsigned int info; /* SNDRV_PCM_INFO_* */ > u64 formats; /* SNDRV_PCM_FMTBIT_* */ > unsigned int rates; /* SNDRV_PCM_RATE_* */ >@@ -82,7 +82,7 @@ typedef struct _snd_pcm_hardware { > size_t fifo_size; /* fifo size in bytes */ > } snd_pcm_hardware_t; > >-typedef struct _snd_pcm_ops { >+typedef struct snd_pcm_ops { > int (*open)(snd_pcm_substream_t *substream); > int (*close)(snd_pcm_substream_t *substream); > int (*ioctl)(snd_pcm_substream_t * substream, >@@ -272,13 +272,13 @@ typedef struct { > ratden_t *rats; > } snd_pcm_hw_constraint_ratdens_t; > >-typedef struct { >+typedef struct snd_pcm_hw_constraint_list { > unsigned int count; > unsigned int *list; > unsigned int mask; > } snd_pcm_hw_constraint_list_t; > >-struct _snd_pcm_runtime { >+struct snd_pcm_runtime { > /* -- Status -- */ > snd_pcm_substream_t *trigger_master; > snd_timestamp_t trigger_tstamp; /* trigger timestamp */ >@@ -366,8 +366,8 @@ typedef struct _snd_pcm_group { /* keep linked substreams */ > struct list_head substreams; > } snd_pcm_group_t; > >-struct _snd_pcm_substream { >- snd_pcm_t *pcm; >+struct snd_pcm_substream { >+ struct snd_pcm *pcm; > snd_pcm_str_t *pstr; > void *private_data; /* copied from pcm->private_data */ > int number; >@@ -436,7 +436,7 @@ struct _snd_pcm_str { > #endif > }; > >-struct _snd_pcm { >+struct snd_pcm { > snd_card_t *card; > unsigned int device; /* device number */ > unsigned int info_flags; >@@ -448,7 +448,7 @@ struct _snd_pcm { > struct semaphore open_mutex; > wait_queue_head_t open_wait; > void *private_data; >- void (*private_free) (snd_pcm_t *pcm); >+ void (*private_free) (struct snd_pcm *pcm); > #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) > snd_pcm_oss_t oss; > #endif >@@ -470,7 +470,7 @@ extern snd_minor_t snd_pcm_reg[2]; > > int snd_pcm_new(snd_card_t * card, char *id, int device, > int playback_count, int capture_count, >- snd_pcm_t **rpcm); >+ struct snd_pcm **rpcm); > int snd_pcm_new_stream(snd_pcm_t *pcm, int stream, int substream_count); > > int snd_pcm_notify(snd_pcm_notify_t *notify, int nfree); >@@ -489,7 +489,7 @@ int snd_pcm_start(snd_pcm_substream_t *substream); > int snd_pcm_stop(snd_pcm_substream_t *substream, int status); > #ifdef CONFIG_PM > int snd_pcm_suspend(snd_pcm_substream_t *substream); >-int snd_pcm_suspend_all(snd_pcm_t *pcm); >+int snd_pcm_suspend_all(struct snd_pcm *pcm); > #endif > int snd_pcm_kernel_playback_ioctl(snd_pcm_substream_t *substream, unsigned int cmd, void *arg); > int snd_pcm_kernel_capture_ioctl(snd_pcm_substream_t *substream, unsigned int cmd, void *arg); >@@ -628,6 +628,9 @@ do { \ > #define snd_pcm_group_substream_entry(pos) \ > list_entry(pos, snd_pcm_substream_t, link_list) > >+#define snd_pcm_group_for_each_entry(s, substream) \ >+ list_for_each_entry(s, &substream->group->substreams, link_list) >+ > static inline int snd_pcm_running(snd_pcm_substream_t *substream) > { > return (substream->runtime->status->state == SNDRV_PCM_STATE_RUNNING || >@@ -861,7 +864,7 @@ ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples); > const char *snd_pcm_format_name(snd_pcm_format_t format); > const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat); > >-void snd_pcm_set_ops(snd_pcm_t * pcm, int direction, snd_pcm_ops_t *ops); >+void snd_pcm_set_ops(struct snd_pcm * pcm, int direction, snd_pcm_ops_t *ops); > void snd_pcm_set_sync(snd_pcm_substream_t * substream); > int snd_pcm_lib_interleave_len(snd_pcm_substream_t *substream); > int snd_pcm_lib_ioctl(snd_pcm_substream_t *substream, >@@ -925,11 +928,11 @@ void snd_pcm_timer_done(snd_pcm_substream_t * substream); > */ > > int snd_pcm_lib_preallocate_free(snd_pcm_substream_t *substream); >-int snd_pcm_lib_preallocate_free_for_all(snd_pcm_t *pcm); >+int snd_pcm_lib_preallocate_free_for_all(struct snd_pcm *pcm); > int snd_pcm_lib_preallocate_pages(snd_pcm_substream_t *substream, > int type, struct device *data, > size_t size, size_t max); >-int snd_pcm_lib_preallocate_pages_for_all(snd_pcm_t *pcm, >+int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm, > int type, void *data, > size_t size, size_t max); > int snd_pcm_lib_malloc_pages(snd_pcm_substream_t *substream, size_t size); >diff --git a/redhat/configs/config-generic b/redhat/configs/config-generic >index b9b574b..9b41c08 100644 >--- a/redhat/configs/config-generic >+++ b/redhat/configs/config-generic >@@ -1880,6 +1880,7 @@ CONFIG_SND_FM801_TEA575X=m > CONFIG_SND_INTEL8X0M=m > CONFIG_SND_PDAUDIOCF=m > CONFIG_SND_HDA_INTEL=m >+CONFIG_SND_HDA_POWER_SAVE_DEFAULT=0 > # > # ALSA USB devices > # >diff --git a/sound/core/Kconfig b/sound/core/Kconfig >index 732208b..2581e93 100644 >--- a/sound/core/Kconfig >+++ b/sound/core/Kconfig >@@ -98,3 +98,5 @@ config SND_DEBUG_DETECT > bool "Debug detection" > depends on SND_DEBUG > >+config SND_VMASTER >+ bool >diff --git a/sound/core/Makefile b/sound/core/Makefile >index 37e2fe6..fb6aa7e 100644 >--- a/sound/core/Makefile >+++ b/sound/core/Makefile >@@ -11,6 +11,7 @@ endif > ifeq ($(CONFIG_SND_OSSEMUL),y) > snd-objs += sound_oss.o info_oss.o > endif >+snd-$(CONFIG_SND_VMASTER) += vmaster.o > > snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \ > pcm_memory.o >diff --git a/sound/core/control.c b/sound/core/control.c >index c9c586c..705bc52 100644 >--- a/sound/core/control.c >+++ b/sound/core/control.c >@@ -1331,3 +1331,30 @@ int snd_ctl_unregister(snd_card_t *card) > > EXPORT_SYMBOL(snd_ctl_elem_read); > EXPORT_SYMBOL(snd_ctl_elem_write); >+ >+/* >+ * Frequently used control callbacks >+ */ >+int snd_ctl_boolean_mono_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; >+} >+ >+EXPORT_SYMBOL(snd_ctl_boolean_mono_info); >+ >+int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol, >+ struct snd_ctl_elem_info *uinfo) >+{ >+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; >+ uinfo->count = 2; >+ uinfo->value.integer.min = 0; >+ uinfo->value.integer.max = 1; >+ return 0; >+} >+ >+EXPORT_SYMBOL(snd_ctl_boolean_stereo_info); >diff --git a/sound/core/ioctl32/ioctl32.c b/sound/core/ioctl32/ioctl32.c >index b36375f..0e30c94 100644 >--- a/sound/core/ioctl32/ioctl32.c >+++ b/sound/core/ioctl32/ioctl32.c >@@ -142,7 +142,7 @@ DEFINE_ALSA_IOCTL_ENTRY(ctl_elem_list, ctl_elem_list, SNDRV_CTL_IOCTL_ELEM_LIST) > */ > > struct sndrv_ctl_elem_info32 { >- struct sndrv_ctl_elem_id id; // the size of struct is same >+ struct snd_ctl_elem_id id; // the size of struct is same > s32 type; > u32 access; > u32 count; >@@ -224,7 +224,7 @@ static inline int _snd_ioctl32_ctl_elem_info(unsigned int fd, unsigned int cmd, > DEFINE_ALSA_IOCTL_ENTRY(ctl_elem_info, ctl_elem_info, SNDRV_CTL_IOCTL_ELEM_INFO); > > struct sndrv_ctl_elem_value32 { >- struct sndrv_ctl_elem_id id; >+ struct snd_ctl_elem_id id; > unsigned int indirect; /* bit-field causes misalignment */ > union { > union { >diff --git a/sound/core/ioctl32/ioctl32.h b/sound/core/ioctl32/ioctl32.h >index 3e1a6aa..ada9a61 100644 >--- a/sound/core/ioctl32/ioctl32.h >+++ b/sound/core/ioctl32/ioctl32.h >@@ -38,6 +38,13 @@ > CVT_##sndrv_##type();\ > } > >+#define convert_from_32_1(type, dstp, srcp)\ >+{\ >+ struct snd_##type *dst = dstp;\ >+ struct sndrv_##type##32 *src = srcp;\ >+ CVT_##sndrv_##type();\ >+} >+ > #define convert_to_32(type, dstp, srcp)\ > {\ > struct sndrv_##type *src = srcp;\ >@@ -45,6 +52,13 @@ > CVT_##sndrv_##type();\ > } > >+#define convert_to_32_1(type, dstp, srcp)\ >+{\ >+ struct snd_##type *src = srcp;\ >+ struct sndrv_##type##32 *dst = dstp;\ >+ CVT_##sndrv_##type();\ >+} >+ > > #define DEFINE_ALSA_IOCTL(type) \ > static inline int _snd_ioctl32_##type(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file, unsigned int native_ctl)\ >diff --git a/sound/core/ioctl32/pcm32.c b/sound/core/ioctl32/pcm32.c >index 1bbe3a0..1cea797 100644 >--- a/sound/core/ioctl32/pcm32.c >+++ b/sound/core/ioctl32/pcm32.c >@@ -195,7 +195,7 @@ static void recalculate_boundary(struct file *file) > static inline int _snd_ioctl32_pcm_hw_params(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file, unsigned int native_ctl) > { > struct sndrv_pcm_hw_params32 *data32; >- struct sndrv_pcm_hw_params *data; >+ struct snd_pcm_hw_params *data; > mm_segment_t oldseg; > int err; > >@@ -210,7 +210,7 @@ static inline int _snd_ioctl32_pcm_hw_params(unsigned int fd, unsigned int cmd, > goto __end; > } > memset(data, 0, sizeof(*data)); >- convert_from_32(pcm_hw_params, data, data32); >+ convert_from_32_1(pcm_hw_params, data, data32); > oldseg = get_fs(); > set_fs(KERNEL_DS); > err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)data); >@@ -218,7 +218,7 @@ static inline int _snd_ioctl32_pcm_hw_params(unsigned int fd, unsigned int cmd, > if (err < 0) > goto __end; > err = 0; >- convert_to_32(pcm_hw_params, data32, data); >+ convert_to_32_1(pcm_hw_params, data32, data); > if (copy_to_user((void __user *)arg, data32, sizeof(*data32))) > err = -EFAULT; > else >@@ -404,7 +404,7 @@ static void snd_pcm_hw_convert_to_old_params(struct sndrv_pcm_hw_params_old32 *o > static inline int _snd_ioctl32_pcm_hw_params_old(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file, unsigned int native_ctl) > { > struct sndrv_pcm_hw_params_old32 *data32; >- struct sndrv_pcm_hw_params *data; >+ struct snd_pcm_hw_params *data; > mm_segment_t oldseg; > int err; > >diff --git a/sound/core/vmaster.c b/sound/core/vmaster.c >new file mode 100644 >index 0000000..355bfa9 >--- /dev/null >+++ b/sound/core/vmaster.c >@@ -0,0 +1,352 @@ >+/* >+ * Virtual master and slave controls >+ * >+ * Copyright (c) 2008 by 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, version 2. >+ * >+ */ >+ >+#include <sound/driver.h> >+#include <linux/slab.h> >+#include <sound/core.h> >+#include <sound/control.h> >+ >+/* >+ * a subset of information returned via ctl info callback >+ */ >+struct link_ctl_info { >+ int type; /* value type */ >+ int count; /* item count */ >+ int min_val, max_val; /* min, max values */ >+}; >+ >+/* >+ * link master - this contains a list of slave controls that are >+ * identical types, i.e. info returns the same value type and value >+ * ranges, but may have different number of counts. >+ * >+ * The master control is so far only mono volume/switch for simplicity. >+ * The same value will be applied to all slaves. >+ */ >+struct link_master { >+ struct list_head slaves; >+ struct link_ctl_info info; >+ int val; /* the master value */ >+}; >+ >+/* >+ * link slave - this contains a slave control element >+ * >+ * It fakes the control callbacsk with additional attenuation by the >+ * master control. A slave may have either one or two channels. >+ */ >+ >+struct link_slave { >+ struct list_head list; >+ struct link_master *master; >+ struct link_ctl_info info; >+ int vals[2]; /* current values */ >+ struct snd_kcontrol slave; /* the copy of original control entry */ >+}; >+ >+/* get the slave ctl info and save the initial values */ >+static int slave_init(struct link_slave *slave) >+{ >+ struct snd_ctl_elem_info *uinfo; >+ struct snd_ctl_elem_value *uctl; >+ int err, ch; >+ >+ if (slave->info.count) >+ return 0; /* already initialized */ >+ >+ uinfo = kmalloc(sizeof(*uinfo), GFP_KERNEL); >+ if (!uinfo) >+ return -ENOMEM; >+ uinfo->id = slave->slave.id; >+ err = slave->slave.info(&slave->slave, uinfo); >+ if (err < 0) { >+ kfree(uinfo); >+ return err; >+ } >+ slave->info.type = uinfo->type; >+ slave->info.count = uinfo->count; >+ if (slave->info.count > 2 || >+ (slave->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER && >+ slave->info.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN)) { >+ snd_printk(KERN_ERR "invalid slave element\n"); >+ kfree(uinfo); >+ return -EINVAL; >+ } >+ slave->info.min_val = uinfo->value.integer.min; >+ slave->info.max_val = uinfo->value.integer.max; >+ kfree(uinfo); >+ >+ uctl = kmalloc(sizeof(*uctl), GFP_KERNEL); >+ if (!uctl) >+ return -ENOMEM; >+ uctl->id = slave->slave.id; >+ err = slave->slave.get(&slave->slave, uctl); >+ for (ch = 0; ch < slave->info.count; ch++) >+ slave->vals[ch] = uctl->value.integer.value[ch]; >+ kfree(uctl); >+ return 0; >+} >+ >+/* initialize master volume */ >+static int master_init(struct link_master *master) >+{ >+ struct link_slave *slave; >+ >+ if (master->info.count) >+ return 0; /* already initialized */ >+ >+ list_for_each_entry(slave, &master->slaves, list) { >+ int err = slave_init(slave); >+ if (err < 0) >+ return err; >+ master->info = slave->info; >+ master->info.count = 1; /* always mono */ >+ /* set full volume as default (= no attenuation) */ >+ master->val = master->info.max_val; >+ return 0; >+ } >+ return -ENOENT; >+} >+ >+static int slave_get_val(struct link_slave *slave, >+ struct snd_ctl_elem_value *ucontrol) >+{ >+ int err, ch; >+ >+ err = slave_init(slave); >+ if (err < 0) >+ return err; >+ for (ch = 0; ch < slave->info.count; ch++) >+ ucontrol->value.integer.value[ch] = slave->vals[ch]; >+ return 0; >+} >+ >+static int slave_put_val(struct link_slave *slave, >+ struct snd_ctl_elem_value *ucontrol) >+{ >+ int err, ch, vol; >+ >+ err = master_init(slave->master); >+ if (err < 0) >+ return err; >+ >+ switch (slave->info.type) { >+ case SNDRV_CTL_ELEM_TYPE_BOOLEAN: >+ for (ch = 0; ch < slave->info.count; ch++) >+ ucontrol->value.integer.value[ch] &= >+ !!slave->master->val; >+ break; >+ case SNDRV_CTL_ELEM_TYPE_INTEGER: >+ for (ch = 0; ch < slave->info.count; ch++) { >+ /* max master volume is supposed to be 0 dB */ >+ vol = ucontrol->value.integer.value[ch]; >+ vol += slave->master->val - slave->master->info.max_val; >+ if (vol < slave->info.min_val) >+ vol = slave->info.min_val; >+ else if (vol > slave->info.max_val) >+ vol = slave->info.max_val; >+ ucontrol->value.integer.value[ch] = vol; >+ } >+ break; >+ } >+ return slave->slave.put(&slave->slave, ucontrol); >+} >+ >+/* >+ * ctl callbacks for slaves >+ */ >+static int slave_info(struct snd_kcontrol *kcontrol, >+ struct snd_ctl_elem_info *uinfo) >+{ >+ struct link_slave *slave = snd_kcontrol_chip(kcontrol); >+ return slave->slave.info(&slave->slave, uinfo); >+} >+ >+static int slave_get(struct snd_kcontrol *kcontrol, >+ struct snd_ctl_elem_value *ucontrol) >+{ >+ struct link_slave *slave = snd_kcontrol_chip(kcontrol); >+ return slave_get_val(slave, ucontrol); >+} >+ >+static int slave_put(struct snd_kcontrol *kcontrol, >+ struct snd_ctl_elem_value *ucontrol) >+{ >+ struct link_slave *slave = snd_kcontrol_chip(kcontrol); >+ int err, ch, changed = 0; >+ >+ err = slave_init(slave); >+ if (err < 0) >+ return err; >+ for (ch = 0; ch < slave->info.count; ch++) { >+ if (slave->vals[ch] != ucontrol->value.integer.value[ch]) { >+ changed = 1; >+ slave->vals[ch] = ucontrol->value.integer.value[ch]; >+ } >+ } >+ if (!changed) >+ return 0; >+ return slave_put_val(slave, ucontrol); >+} >+ >+static void slave_free(struct snd_kcontrol *kcontrol) >+{ >+ struct link_slave *slave = snd_kcontrol_chip(kcontrol); >+ if (slave->slave.private_free) >+ slave->slave.private_free(&slave->slave); >+ if (slave->master) >+ list_del(&slave->list); >+ kfree(slave); >+} >+ >+/* >+ * Add a slave control to the group with the given master control >+ * >+ * All slaves must be the same type (returning the same information >+ * via info callback). The fucntion doesn't check it, so it's your >+ * responsibility. >+ * >+ * Also, some additional limitations: >+ * - at most two channels >+ * - logarithmic volume control (dB level), no linear volume >+ * - master can only attenuate the volume, no gain >+ */ >+int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave) >+{ >+ struct link_master *master_link = snd_kcontrol_chip(master); >+ struct link_slave *srec; >+ >+ srec = kzalloc(sizeof(*srec) + >+ slave->count * sizeof(*slave->vd), GFP_KERNEL); >+ if (!srec) >+ return -ENOMEM; >+ srec->slave = *slave; >+ memcpy(srec->slave.vd, slave->vd, slave->count * sizeof(*slave->vd)); >+ srec->master = master_link; >+ >+ /* override callbacks */ >+ slave->info = slave_info; >+ slave->get = slave_get; >+ slave->put = slave_put; >+ slave->private_data = srec; >+ slave->private_free = slave_free; >+ >+ list_add_tail(&srec->list, &master_link->slaves); >+ return 0; >+} >+ >+EXPORT_SYMBOL(snd_ctl_add_slave); >+ >+/* >+ * ctl callbacks for master controls >+ */ >+static int master_info(struct snd_kcontrol *kcontrol, >+ struct snd_ctl_elem_info *uinfo) >+{ >+ struct link_master *master = snd_kcontrol_chip(kcontrol); >+ int ret; >+ >+ ret = master_init(master); >+ if (ret < 0) >+ return ret; >+ uinfo->type = master->info.type; >+ uinfo->count = master->info.count; >+ uinfo->value.integer.min = master->info.min_val; >+ uinfo->value.integer.max = master->info.max_val; >+ return 0; >+} >+ >+static int master_get(struct snd_kcontrol *kcontrol, >+ struct snd_ctl_elem_value *ucontrol) >+{ >+ struct link_master *master = snd_kcontrol_chip(kcontrol); >+ int err = master_init(master); >+ if (err < 0) >+ return err; >+ ucontrol->value.integer.value[0] = master->val; >+ return 0; >+} >+ >+static int master_put(struct snd_kcontrol *kcontrol, >+ struct snd_ctl_elem_value *ucontrol) >+{ >+ struct link_master *master = snd_kcontrol_chip(kcontrol); >+ struct link_slave *slave; >+ struct snd_ctl_elem_value *uval; >+ int err, old_val; >+ >+ err = master_init(master); >+ if (err < 0) >+ return err; >+ old_val = master->val; >+ if (ucontrol->value.integer.value[0] == old_val) >+ return 0; >+ >+ uval = kmalloc(sizeof(*uval), GFP_KERNEL); >+ if (!uval) >+ return -ENOMEM; >+ list_for_each_entry(slave, &master->slaves, list) { >+ master->val = old_val; >+ uval->id = slave->slave.id; >+ slave_get_val(slave, uval); >+ master->val = ucontrol->value.integer.value[0]; >+ slave_put_val(slave, uval); >+ } >+ kfree(uval); >+ return 1; >+} >+ >+static void master_free(struct snd_kcontrol *kcontrol) >+{ >+ struct link_master *master = snd_kcontrol_chip(kcontrol); >+ struct link_slave *slave; >+ >+ list_for_each_entry(slave, &master->slaves, list) >+ slave->master = NULL; >+ kfree(master); >+} >+ >+ >+/* >+ * Create a virtual master control with the given name >+ */ >+struct snd_kcontrol *snd_ctl_make_virtual_master(char *name, >+ const unsigned int *tlv) >+{ >+ struct link_master *master; >+ struct snd_kcontrol *kctl; >+ struct snd_kcontrol_new knew; >+ >+ memset(&knew, 0, sizeof(knew)); >+ knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER; >+ knew.name = name; >+ knew.info = master_info; >+ >+ master = kzalloc(sizeof(*master), GFP_KERNEL); >+ if (!master) >+ return NULL; >+ INIT_LIST_HEAD(&master->slaves); >+ >+ kctl = snd_ctl_new1(&knew, master); >+ if (!kctl) { >+ kfree(master); >+ return NULL; >+ } >+ /* override some callbacks */ >+ kctl->info = master_info; >+ kctl->get = master_get; >+ kctl->put = master_put; >+ kctl->private_free = master_free; >+ >+ return kctl; >+} >+ >+EXPORT_SYMBOL(snd_ctl_make_virtual_master); >diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig >index 9b79c12..a20816a 100644 >--- a/sound/pci/Kconfig >+++ b/sound/pci/Kconfig >@@ -159,10 +159,28 @@ config SND_RME9652 > Say 'Y' or 'M' to include support for RME Hammerfall (RME Digi9652 / > Digi9636) soundcards. > >+config SND_HDA_POWER_SAVE >+ bool "Aggressive power-saving on HD-audio" >+ help >+ Say Y here to enable more aggressive power-saving mode on >+ HD-audio driver. The power-saving timeout can be configured >+ via power_save option or over sysfs on-the-fly. >+ >+config SND_HDA_POWER_SAVE_DEFAULT >+ int "Default time-out for HD-audio power-save mode" >+ depends on SND_HDA_POWER_SAVE >+ default 0 >+ help >+ The default time-out value in seconds for HD-audio automatic >+ power-save mode. 0 means to disable the power-save mode. >+ > config SND_HDA_INTEL > tristate "Intel HD Audio" > depends on SND > select SND_PCM >+ select SND_VMASTER >+ select SND_HDA_POWER_SAVE >+ select SND_HWDEP > help > Say Y here to include support for Intel "High Definition > Audio" (Azalia) motherboard devices. >diff --git a/sound/pci/Makefile b/sound/pci/Makefile >index 2a50055..f451683 100644 >--- a/sound/pci/Makefile >+++ b/sound/pci/Makefile >@@ -48,7 +48,7 @@ obj-$(CONFIG_SND) += \ > ac97/ \ > ali5451/ \ > au88x0/ \ >- azx/ \ >+ hda/ \ > cs46xx/ \ > emu10k1/ \ > ice1712/ \ >diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile >new file mode 100644 >index 0000000..e4bfa2d >--- /dev/null >+++ b/sound/pci/hda/Makefile >@@ -0,0 +1,18 @@ >+snd-hda-intel-y := hda_intel.o >+# since snd-hda-intel is the only driver using hda-codec, >+# merge it into a single module although it was originally >+# designed to be individual modules >+snd-hda-intel-y += hda_codec.o >+snd-hda-intel-y += hda_hwdep.o >+snd-hda-intel-$(CONFIG_PROC_FS) += hda_proc.o >+snd-hda-intel-y += hda_generic.o >+snd-hda-intel-y += patch_realtek.o >+snd-hda-intel-y += patch_cmedia.o >+snd-hda-intel-y += patch_analog.o >+snd-hda-intel-y += patch_sigmatel.o >+snd-hda-intel-y += patch_si3054.o >+snd-hda-intel-y += patch_atihdmi.o >+snd-hda-intel-y += patch_conexant.o >+snd-hda-intel-y += patch_via.o >+ >+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o >diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c >new file mode 100644 >index 0000000..00c15a8 >--- /dev/null >+++ b/sound/pci/hda/hda_codec.c >@@ -0,0 +1,3092 @@ >+/* >+ * 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 <sound/driver.h> >+#include <linux/init.h> >+#include <linux/delay.h> >+#include <linux/slab.h> >+#include <linux/pci.h> >+#include <linux/mutex.h> >+#include <sound/core.h> >+#include "hda_codec.h" >+#include <sound/asoundef.h> >+#include <sound/tlv.h> >+#include <sound/initval.h> >+#include "hda_local.h" >+#include <sound/hda_hwdep.h> >+#include "hda_patch.h" /* codec presets */ >+ >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+/* define this option here to hide as static */ >+static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT; >+module_param(power_save, int, 0644); >+MODULE_PARM_DESC(power_save, "Automatic power-saving timeout " >+ "(in second, 0 = disable)."); >+#endif >+ >+/* >+ * vendor / preset table >+ */ >+ >+struct hda_vendor_id { >+ unsigned int id; >+ const char *name; >+}; >+ >+/* codec vendor labels */ >+static struct hda_vendor_id hda_vendor_ids[] = { >+ { 0x1002, "ATI" }, >+ { 0x1057, "Motorola" }, >+ { 0x1095, "Silicon Image" }, >+ { 0x10ec, "Realtek" }, >+ { 0x1106, "VIA" }, >+ { 0x111d, "IDT" }, >+ { 0x11c1, "LSI" }, >+ { 0x11d4, "Analog Devices" }, >+ { 0x13f6, "C-Media" }, >+ { 0x14f1, "Conexant" }, >+ { 0x17e8, "Chrontel" }, >+ { 0x1854, "LG" }, >+ { 0x434d, "C-Media" }, >+ { 0x8384, "SigmaTel" }, >+ {} /* terminator */ >+}; >+ >+static const struct hda_codec_preset *hda_preset_tables[] = { >+ snd_hda_preset_realtek, >+ snd_hda_preset_cmedia, >+ snd_hda_preset_analog, >+ snd_hda_preset_sigmatel, >+ snd_hda_preset_si3054, >+ snd_hda_preset_atihdmi, >+ snd_hda_preset_conexant, >+ snd_hda_preset_via, >+ NULL >+}; >+ >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+static void hda_power_work(void *); >+static void hda_keep_power_on(struct hda_codec *codec); >+#else >+static inline void hda_keep_power_on(struct hda_codec *codec) {} >+#endif >+ >+/** >+ * 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 int res; >+ snd_hda_power_up(codec); >+ mutex_lock(&codec->bus->cmd_mutex); >+ if (!codec->bus->ops.command(codec, nid, direct, verb, parm)) >+ res = codec->bus->ops.get_response(codec); >+ else >+ res = (unsigned int)-1; >+ mutex_unlock(&codec->bus->cmd_mutex); >+ snd_hda_power_down(codec); >+ return res; >+} >+ >+/** >+ * 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) >+{ >+ int err; >+ snd_hda_power_up(codec); >+ mutex_lock(&codec->bus->cmd_mutex); >+ err = codec->bus->ops.command(codec, nid, direct, verb, parm); >+ mutex_unlock(&codec->bus->cmd_mutex); >+ snd_hda_power_down(codec); >+ return err; >+} >+ >+/** >+ * 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); >+} >+ >+/** >+ * 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); >+} >+ >+/** >+ * snd_hda_get_connections - get 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) >+{ >+ unsigned int parm; >+ int i, conn_len, conns; >+ unsigned int shift, num_elems, mask; >+ hda_nid_t prev_nid; >+ >+ snd_assert(conn_list && max_conns > 0, return -EINVAL); >+ >+ 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); >+ 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); >+ range_val = !!(parm & (1 << (shift-1))); /* ranges */ >+ val = parm & mask; >+ 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 >+ "Too many connections\n"); >+ return -EINVAL; >+ } >+ conn_list[conns++] = n; >+ } >+ } else { >+ if (conns >= max_conns) { >+ snd_printk(KERN_ERR "Too many connections\n"); >+ return -EINVAL; >+ } >+ conn_list[conns++] = val; >+ } >+ prev_nid = val; >+ } >+ return conns; >+} >+ >+ >+/** >+ * 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; >+ >+ 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; >+ >+ schedule_work(&unsol->work); >+ >+ return 0; >+} >+ >+/* >+ * process queueud unsolicited events >+ */ >+static void process_unsol_events(void *data) >+{ >+ struct work_struct *work = data; >+ 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 __devinit 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->work); >+ 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->unsol) { >+ flush_scheduled_work(); >+ 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); >+ kfree(bus); >+ return 0; >+} >+ >+static int snd_hda_bus_dev_free(struct snd_device *device) >+{ >+ struct hda_bus *bus = device->device_data; >+ return snd_hda_bus_free(bus); >+} >+ >+/** >+ * 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_free = snd_hda_bus_dev_free, >+ }; >+ >+ snd_assert(temp, return -EINVAL); >+ snd_assert(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->ops = temp->ops; >+ >+ mutex_init(&bus->cmd_mutex); >+ INIT_LIST_HEAD(&bus->codec_list); >+ >+ 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; >+} >+ >+#ifdef CONFIG_SND_HDA_GENERIC >+#define is_generic_config(codec) \ >+ (codec->bus->modelname && !strcmp(codec->bus->modelname, "generic")) >+#else >+#define is_generic_config(codec) 0 >+#endif >+ >+/* >+ * find a matching codec preset >+ */ >+static const struct hda_codec_preset __devinit * >+find_codec_preset(struct hda_codec *codec) >+{ >+ const struct hda_codec_preset **tbl, *preset; >+ >+ if (is_generic_config(codec)) >+ return NULL; /* use the generic parser */ >+ >+ for (tbl = hda_preset_tables; *tbl; tbl++) { >+ for (preset = *tbl; 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)) >+ return preset; >+ } >+ } >+ return NULL; >+} >+ >+/* >+ * snd_hda_get_codec_name - store the codec name >+ */ >+void snd_hda_get_codec_name(struct hda_codec *codec, >+ char *name, int namelen) >+{ >+ const struct hda_vendor_id *c; >+ const char *vendor = NULL; >+ u16 vendor_id = codec->vendor_id >> 16; >+ char tmp[16]; >+ >+ 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; >+ } >+ if (codec->preset && codec->preset->name) >+ snprintf(name, namelen, "%s %s", vendor, codec->preset->name); >+ else >+ snprintf(name, namelen, "%s ID %x", vendor, >+ codec->vendor_id & 0xffff); >+} >+ >+/* >+ * look for an AFG and MFG nodes >+ */ >+static void __devinit setup_fg_nodes(struct hda_codec *codec) >+{ >+ int i, total_nodes; >+ hda_nid_t nid; >+ >+ total_nodes = snd_hda_get_sub_nodes(codec, AC_NODE_ROOT, &nid); >+ for (i = 0; i < total_nodes; i++, nid++) { >+ unsigned int func; >+ func = snd_hda_param_read(codec, nid, AC_PAR_FUNCTION_TYPE); >+ switch (func & 0xff) { >+ case AC_GRP_AUDIO_FUNCTION: >+ codec->afg = nid; >+ break; >+ case AC_GRP_MODEM_FUNCTION: >+ codec->mfg = nid; >+ 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; >+} >+ >+ >+static void init_hda_cache(struct hda_cache_rec *cache, >+ unsigned int record_size); >+static void free_hda_cache(struct hda_cache_rec *cache); >+ >+/* >+ * codec destructor >+ */ >+static void snd_hda_codec_free(struct hda_codec *codec) >+{ >+ if (!codec) >+ return; >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+ cancel_delayed_work(&codec->power_work); >+ flush_scheduled_work(); >+#endif >+ list_del(&codec->list); >+ codec->bus->caddr_tbl[codec->addr] = NULL; >+ if (codec->patch_ops.free) >+ codec->patch_ops.free(codec); >+ free_hda_cache(&codec->amp_cache); >+ free_hda_cache(&codec->cmd_cache); >+ kfree(codec->wcaps); >+ kfree(codec); >+} >+ >+/** >+ * 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[13]; >+ int err; >+ >+ snd_assert(bus, return -EINVAL); >+ snd_assert(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); >+ init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info)); >+ init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head)); >+ >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+ INIT_WORK(&codec->power_work, hda_power_work, codec); >+ /* 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"); >+ snd_hda_codec_free(codec); >+ return -ENODEV; >+ } >+ >+ if (read_widget_caps(codec, codec->afg ? codec->afg : codec->mfg) < 0) { >+ snd_printk(KERN_ERR "hda_codec: cannot malloc\n"); >+ snd_hda_codec_free(codec); >+ return -ENOMEM; >+ } >+ >+ 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); >+ } >+ >+ codec->preset = find_codec_preset(codec); >+ /* audio codec should override the mixer name */ >+ if (codec->afg || !*bus->card->mixername) >+ snd_hda_get_codec_name(codec, bus->card->mixername, >+ sizeof(bus->card->mixername)); >+ >+ if (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 < 0) { >+ snd_hda_codec_free(codec); >+ return err; >+ } >+ >+ if (codec->patch_ops.unsol_event) >+ init_unsol_queue(bus); >+ >+ snd_hda_codec_proc_new(codec); >+#ifdef CONFIG_SND_HDA_HWDEP >+ snd_hda_create_hwdep(codec); >+#endif >+ >+ sprintf(component, "HDA:%08x", codec->vendor_id); >+ snd_component_add(codec->bus->card, component); >+ >+ if (codecp) >+ *codecp = codec; >+ return 0; >+} >+ >+/** >+ * 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) >+{ >+ 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); >+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, >+ (stream_tag << 4) | channel_id); >+ msleep(1); >+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, format); >+} >+ >+void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid) >+{ >+ if (!nid) >+ return; >+ >+ snd_printdd("hda_codec_cleanup_stream: NID=0x%x\n", nid); >+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0); >+#if 0 /* keep the format */ >+ msleep(1); >+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0); >+#endif >+} >+ >+/* >+ * amp access functions >+ */ >+ >+/* FIXME: more better hash key? */ >+#define HDA_HASH_KEY(nid,dir,idx) (u32)((nid) + ((idx) << 16) + ((dir) << 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)); >+ cache->record_size = record_size; >+} >+ >+static void free_hda_cache(struct hda_cache_rec *cache) >+{ >+ kfree(cache->buffer); >+} >+ >+/* query the hash. allocate an entry if not found. */ >+static struct hda_cache_head *get_alloc_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 = (struct hda_cache_head *)(cache->buffer + >+ cur * cache->record_size); >+ if (info->key == key) >+ return info; >+ cur = info->next; >+ } >+ >+ /* add a new hash entry */ >+ if (cache->num_entries >= cache->size) { >+ /* reallocate the array */ >+ unsigned int new_size = cache->size + 64; >+ void *new_buffer; >+ new_buffer = kcalloc(new_size, cache->record_size, GFP_KERNEL); >+ if (!new_buffer) { >+ snd_printk(KERN_ERR "hda_codec: " >+ "can't malloc amp_info\n"); >+ return NULL; >+ } >+ if (cache->buffer) { >+ memcpy(new_buffer, cache->buffer, >+ cache->size * cache->record_size); >+ kfree(cache->buffer); >+ } >+ cache->size = new_size; >+ cache->buffer = new_buffer; >+ } >+ cur = cache->num_entries++; >+ info = (struct hda_cache_head *)(cache->buffer + >+ cur * cache->record_size); >+ info->key = key; >+ info->val = 0; >+ 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 capabilities for the given widget and direction >+ */ >+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; >+} >+ >+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; >+} >+ >+/* >+ * 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; >+ parm |= val; >+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, parm); >+ info->vol[ch] = val; >+} >+ >+/* >+ * 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); >+} >+ >+/* >+ * update the AMP value, mask = bit mask to set, val = the value >+ */ >+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; >+ 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; >+} >+ >+/* >+ * update the AMP stereo 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; >+ for (ch = 0; ch < 2; ch++) >+ ret |= snd_hda_codec_amp_update(codec, nid, ch, direction, >+ idx, mask, val); >+ return ret; >+} >+ >+#ifdef SND_HDA_NEEDS_RESUME >+/* resume the all amp commands from the cache */ >+void snd_hda_codec_resume_amp(struct hda_codec *codec) >+{ >+ struct hda_amp_info *buffer = codec->amp_cache.buffer; >+ int i; >+ >+ for (i = 0; i < codec->amp_cache.size; 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]); >+ } >+ } >+} >+#endif /* SND_HDA_NEEDS_RESUME */ >+ >+/* >+ * AMP control callbacks >+ */ >+/* retrieve parameters from private_value */ >+#define get_amp_nid(kc) ((kc)->private_value & 0xffff) >+#define get_amp_channels(kc) (((kc)->private_value >> 16) & 0x3) >+#define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1) >+#define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf) >+ >+/* volume */ >+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); >+ u32 caps; >+ >+ caps = query_amp_caps(codec, nid, dir); >+ /* num steps */ >+ caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; >+ if (!caps) { >+ printk(KERN_WARNING "hda_codec: " >+ "num_steps = 0 for NID=0x%x (ctl = %s)\n", nid, >+ kcontrol->id.name); >+ return -EINVAL; >+ } >+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; >+ uinfo->count = chs == 3 ? 2 : 1; >+ uinfo->value.integer.min = 0; >+ uinfo->value.integer.max = caps; >+ return 0; >+} >+ >+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); >+ long *valp = ucontrol->value.integer.value; >+ >+ if (chs & 1) >+ *valp++ = snd_hda_codec_amp_read(codec, nid, 0, dir, idx) >+ & HDA_AMP_VOLMASK; >+ if (chs & 2) >+ *valp = snd_hda_codec_amp_read(codec, nid, 1, dir, idx) >+ & HDA_AMP_VOLMASK; >+ return 0; >+} >+ >+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); >+ 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, >+ 0x7f, *valp); >+ valp++; >+ } >+ if (chs & 2) >+ change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx, >+ 0x7f, *valp); >+ snd_hda_power_down(codec); >+ return change; >+} >+ >+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); >+ 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 = ((int)val1) * ((int)val2); >+ 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; >+} >+ >+/* >+ * set (static) TLV for virtual master volume; recalculated as max 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; >+} >+ >+/* 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; >+ strcpy(id.name, name); >+ return snd_ctl_find_id(codec->bus->card, &id); >+} >+ >+struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, >+ const char *name) >+{ >+ return _snd_hda_find_mixer_ctl(codec, name, 0); >+} >+ >+/* create a virtual master control and add slaves */ >+int snd_hda_add_vmaster(struct hda_codec *codec, char *name, >+ unsigned int *tlv, const char **slaves) >+{ >+ struct snd_kcontrol *kctl; >+ const char **s; >+ int err; >+ >+ for (s = slaves; *s && !snd_hda_find_mixer_ctl(codec, *s); s++) >+ ; >+ if (!*s) { >+ 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_ctl_add(codec->bus->card, kctl); >+ if (err < 0) >+ return err; >+ >+ for (s = slaves; *s; s++) { >+ struct snd_kcontrol *sctl; >+ >+ sctl = snd_hda_find_mixer_ctl(codec, *s); >+ if (!sctl) { >+ snd_printdd("Cannot find slave %s, skipped\n", *s); >+ continue; >+ } >+ err = snd_ctl_add_slave(kctl, sctl); >+ if (err < 0) >+ return err; >+ } >+ return 0; >+} >+ >+/* switch */ >+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; >+} >+ >+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; >+} >+ >+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); >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+ if (codec->patch_ops.check_power_status) >+ codec->patch_ops.check_power_status(codec, nid); >+#endif >+ snd_hda_power_down(codec); >+ return change; >+} >+ >+/* >+ * bound volume controls >+ * >+ * bind multiple volumes (# indices, from 0) >+ */ >+ >+#define AMP_VAL_IDX_SHIFT 19 >+#define AMP_VAL_IDX_MASK (0x0f<<19) >+ >+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->spdif_mutex); /* reuse spdif_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->spdif_mutex); >+ return err; >+} >+ >+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->spdif_mutex); /* reuse spdif_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->spdif_mutex); >+ return err < 0 ? err : change; >+} >+ >+/* >+ * generic bound volume/swtich controls >+ */ >+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->spdif_mutex); /* reuse spdif_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->spdif_mutex); >+ return err; >+} >+ >+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->spdif_mutex); /* reuse spdif_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->spdif_mutex); >+ return err; >+} >+ >+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->spdif_mutex); /* reuse spdif_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->spdif_mutex); >+ return err < 0 ? err : change; >+} >+ >+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->spdif_mutex); /* reuse spdif_mutex */ >+ c = (struct hda_bind_ctls *)kcontrol->private_value; >+ kcontrol->private_value = *c->values; >+ err = -EIO; // err = c->ops->tlv(kcontrol, op_flag, size, tlv); >+ kcontrol->private_value = (long)c; >+ mutex_unlock(&codec->spdif_mutex); >+ return err; >+} >+ >+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 >+}; >+ >+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 >+}; >+ >+/* >+ * 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); >+ >+ ucontrol->value.iec958.status[0] = codec->spdif_status & 0xff; >+ ucontrol->value.iec958.status[1] = (codec->spdif_status >> 8) & 0xff; >+ ucontrol->value.iec958.status[2] = (codec->spdif_status >> 16) & 0xff; >+ ucontrol->value.iec958.status[3] = (codec->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; >+} >+ >+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); >+ hda_nid_t nid = kcontrol->private_value; >+ unsigned short val; >+ int change; >+ >+ mutex_lock(&codec->spdif_mutex); >+ codec->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(codec->spdif_status); >+ val |= codec->spdif_ctls & 1; >+ change = codec->spdif_ctls != val; >+ codec->spdif_ctls = val; >+ >+ if (change) { >+ snd_hda_codec_write_cache(codec, nid, 0, >+ AC_VERB_SET_DIGI_CONVERT_1, >+ val & 0xff); >+ snd_hda_codec_write_cache(codec, nid, 0, >+ AC_VERB_SET_DIGI_CONVERT_2, >+ val >> 8); >+ } >+ >+ 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); >+ >+ ucontrol->value.integer.value[0] = codec->spdif_ctls & AC_DIG1_ENABLE; >+ return 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); >+ hda_nid_t nid = kcontrol->private_value; >+ unsigned short val; >+ int change; >+ >+ mutex_lock(&codec->spdif_mutex); >+ val = codec->spdif_ctls & ~AC_DIG1_ENABLE; >+ if (ucontrol->value.integer.value[0]) >+ val |= AC_DIG1_ENABLE; >+ change = codec->spdif_ctls != val; >+ if (change) { >+ codec->spdif_ctls = val; >+ snd_hda_codec_write_cache(codec, nid, 0, >+ AC_VERB_SET_DIGI_CONVERT_1, >+ val & 0xff); >+ /* unmute amp switch (if any) */ >+ if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) && >+ (val & AC_DIG1_ENABLE)) >+ snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, 0); >+ } >+ 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 */ >+}; >+ >+#define SPDIF_MAX_IDX 4 /* 4 instances should be enough to probe */ >+ >+/** >+ * snd_hda_create_spdif_out_ctls - create Output SPDIF-related controls >+ * @codec: the HDA codec >+ * @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 nid) >+{ >+ int err; >+ struct snd_kcontrol *kctl; >+ struct snd_kcontrol_new *dig_mix; >+ int idx; >+ >+ for (idx = 0; idx < SPDIF_MAX_IDX; idx++) { >+ if (!_snd_hda_find_mixer_ctl(codec, "IEC958 Playback Switch", >+ idx)) >+ break; >+ } >+ if (idx >= SPDIF_MAX_IDX) { >+ printk(KERN_ERR "hda_codec: too many IEC958 outputs\n"); >+ return -EBUSY; >+ } >+ for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) { >+ kctl = snd_ctl_new1(dig_mix, codec); >+ kctl->id.index = idx; >+ kctl->private_value = nid; >+ err = snd_ctl_add(codec->bus->card, kctl); >+ if (err < 0) >+ return err; >+ } >+ codec->spdif_ctls = >+ snd_hda_codec_read(codec, nid, 0, >+ AC_VERB_GET_DIGI_CONVERT_1, 0); >+ codec->spdif_status = convert_to_spdif_status(codec->spdif_ctls); >+ return 0; >+} >+ >+/* >+ * SPDIF sharing with analog output >+ */ >+static int spdif_share_sw_get(struct snd_kcontrol *kcontrol, >+ struct snd_ctl_elem_value *ucontrol) >+{ >+ struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol); >+ ucontrol->value.integer.value[0] = mout->share_spdif; >+ return 0; >+} >+ >+static int spdif_share_sw_put(struct snd_kcontrol *kcontrol, >+ struct snd_ctl_elem_value *ucontrol) >+{ >+ struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol); >+ mout->share_spdif = !!ucontrol->value.integer.value[0]; >+ return 0; >+} >+ >+static struct snd_kcontrol_new spdif_share_sw = { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "IEC958 Default PCM Playback Switch", >+ .info = snd_ctl_boolean_mono_info, >+ .get = spdif_share_sw_get, >+ .put = spdif_share_sw_put, >+}; >+ >+int snd_hda_create_spdif_share_sw(struct hda_codec *codec, >+ struct hda_multi_out *mout) >+{ >+ if (!mout->dig_out_nid) >+ return 0; >+ /* ATTENTION: here mout is passed as private_data, instead of codec */ >+ return snd_ctl_add(codec->bus->card, >+ snd_ctl_new1(&spdif_share_sw, mout)); >+} >+ >+/* >+ * SPDIF input >+ */ >+ >+#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; >+ >+ for (idx = 0; idx < SPDIF_MAX_IDX; idx++) { >+ if (!_snd_hda_find_mixer_ctl(codec, "IEC958 Capture Switch", >+ idx)) >+ break; >+ } >+ if (idx >= SPDIF_MAX_IDX) { >+ printk(KERN_ERR "hda_codec: too many IEC958 inputs\n"); >+ return -EBUSY; >+ } >+ for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) { >+ kctl = snd_ctl_new1(dig_mix, codec); >+ kctl->private_value = nid; >+ err = snd_ctl_add(codec->bus->card, 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; >+} >+ >+#ifdef SND_HDA_NEEDS_RESUME >+/* >+ * 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_power_up(codec); >+ mutex_lock(&codec->bus->cmd_mutex); >+ err = codec->bus->ops.command(codec, nid, direct, verb, parm); >+ if (!err) { >+ struct hda_cache_head *c; >+ u32 key = build_cmd_cache_key(nid, verb); >+ c = get_alloc_hash(&codec->cmd_cache, key); >+ if (c) >+ c->val = parm; >+ } >+ mutex_unlock(&codec->bus->cmd_mutex); >+ snd_hda_power_down(codec); >+ return err; >+} >+ >+/* resume the all commands from the cache */ >+void snd_hda_codec_resume_cache(struct hda_codec *codec) >+{ >+ struct hda_cache_head *buffer = codec->cmd_cache.buffer; >+ int i; >+ >+ for (i = 0; i < codec->cmd_cache.size; 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); >+ } >+} >+ >+/** >+ * 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); >+} >+#endif /* SND_HDA_NEEDS_RESUME */ >+ >+/* >+ * set power state of the codec >+ */ >+static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, >+ unsigned int power_state) >+{ >+ hda_nid_t nid; >+ int i; >+ >+ snd_hda_codec_write(codec, fg, 0, AC_VERB_SET_POWER_STATE, >+ power_state); >+ msleep(10); /* partial workaround for "azx_get_response timeout" */ >+ >+ nid = codec->start_nid; >+ for (i = 0; i < codec->num_nodes; i++, nid++) { >+ unsigned int wcaps = get_wcaps(codec, nid); >+ if (wcaps & AC_WCAP_POWER) { >+ unsigned int wid_type = (wcaps & AC_WCAP_TYPE) >> >+ AC_WCAP_TYPE_SHIFT; >+ if (wid_type == AC_WID_PIN) { >+ unsigned int pincap; >+ /* >+ * don't power down the widget if it controls >+ * eapd and EAPD_BTLENABLE is set. >+ */ >+ pincap = snd_hda_param_read(codec, nid, >+ AC_PAR_PIN_CAP); >+ if (pincap & AC_PINCAP_EAPD) { >+ int eapd = snd_hda_codec_read(codec, >+ nid, 0, >+ AC_VERB_GET_EAPD_BTLENABLE, 0); >+ eapd &= 0x02; >+ if (power_state == AC_PWRST_D3 && eapd) >+ 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; >+ msleep(10); >+ /* 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)); >+ } >+} >+ >+#ifdef SND_HDA_NEEDS_RESUME >+/* >+ * 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_set_power_state(codec, >+ codec->afg ? codec->afg : codec->mfg, >+ AC_PWRST_D3); >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+ cancel_delayed_work(&codec->power_work); >+ codec->power_on = 0; >+ codec->power_transition = 0; >+#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); >+ 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 /* SND_HDA_NEEDS_RESUME */ >+ >+ >+/** >+ * 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 = 0; >+ /* fake as if already powered-on */ >+ hda_keep_power_on(codec); >+ /* then fire up */ >+ hda_set_power_state(codec, >+ codec->afg ? codec->afg : codec->mfg, >+ AC_PWRST_D0); >+ /* continue to initialize... */ >+ if (codec->patch_ops.init) >+ err = codec->patch_ops.init(codec); >+ if (!err && codec->patch_ops.build_controls) >+ err = codec->patch_ops.build_controls(codec); >+ snd_hda_power_down(codec); >+ if (err < 0) >+ return err; >+ } >+ >+ return 0; >+} >+ >+/* >+ * stream formats >+ */ >+struct hda_rate_tbl { >+ unsigned int hz; >+ unsigned int alsa_bits; >+ unsigned int hda_fmt; >+}; >+ >+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, 0x0500 }, /* 1/6 x 48 */ >+ { 11025, SNDRV_PCM_RATE_11025, 0x4300 }, /* 1/4 x 44 */ >+ { 16000, SNDRV_PCM_RATE_16000, 0x0200 }, /* 1/3 x 48 */ >+ { 22050, SNDRV_PCM_RATE_22050, 0x4100 }, /* 1/2 x 44 */ >+ { 32000, SNDRV_PCM_RATE_32000, 0x0a00 }, /* 2/3 x 48 */ >+ { 44100, SNDRV_PCM_RATE_44100, 0x4000 }, /* 44 */ >+ { 48000, SNDRV_PCM_RATE_48000, 0x0000 }, /* 48 */ >+ { 88200, SNDRV_PCM_RATE_88200, 0x4800 }, /* 2 x 44 */ >+ { 96000, SNDRV_PCM_RATE_96000, 0x0800 }, /* 2 x 48 */ >+ { 176400, SNDRV_PCM_RATE_176400, 0x5800 },/* 4 x 44 */ >+ { 192000, SNDRV_PCM_RATE_192000, 0x1800 }, /* 4 x 48 */ >+#define AC_PAR_PCM_RATE_BITS 11 >+ /* up to bits 10, 384kHZ isn't supported properly */ >+ >+ /* not autodetected value */ >+ { 9600, SNDRV_PCM_RATE_KNOT, 0x0400 }, /* 1/5 x 48 */ >+ >+ { 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) >+{ >+ 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 |= 0x00; break; >+ case 16: val |= 0x10; break; >+ case 20: >+ case 24: >+ case 32: >+ if (maxbps >= 32) >+ val |= 0x40; >+ else if (maxbps >= 24) >+ val |= 0x30; >+ else >+ val |= 0x20; >+ break; >+ default: >+ snd_printdd("invalid format width %d\n", >+ snd_pcm_format_width(format)); >+ return 0; >+ } >+ >+ return val; >+} >+ >+/** >+ * 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) >+{ >+ int i; >+ unsigned int val, streams; >+ >+ 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 == -1) >+ return -EIO; >+ } >+ if (!val) >+ val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM); >+ >+ 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; >+ } >+ *ratesp = rates; >+ } >+ >+ if (formatsp || bpsp) { >+ u64 formats = 0; >+ unsigned int bps; >+ unsigned int wcaps; >+ >+ wcaps = get_wcaps(codec, nid); >+ streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM); >+ if (streams == -1) >+ return -EIO; >+ if (!streams) { >+ streams = snd_hda_param_read(codec, codec->afg, >+ AC_PAR_STREAM); >+ if (streams == -1) >+ 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; >+ } >+ } >+ else if (streams == AC_SUPFMT_FLOAT32) { >+ /* should be exclusive */ >+ formats |= SNDRV_PCM_FMTBIT_FLOAT_LE; >+ bps = 32; >+ } else 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 (formatsp) >+ *formatsp = formats; >+ if (bpsp) >+ *bpsp = bps; >+ } >+ >+ return 0; >+} >+ >+/** >+ * snd_hda_is_supported_format - check whether the given node supports >+ * the format val >+ * >+ * 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; >+ >+ if (nid != codec->afg && >+ (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) { >+ val = snd_hda_param_read(codec, nid, AC_PAR_PCM); >+ if (val == -1) >+ return 0; >+ } >+ if (!val) { >+ val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM); >+ if (val == -1) >+ 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 = snd_hda_param_read(codec, nid, AC_PAR_STREAM); >+ if (stream == -1) >+ return 0; >+ if (!stream && nid != codec->afg) >+ stream = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM); >+ if (!stream || stream == -1) >+ 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; >+} >+ >+/* >+ * 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 __devinit set_pcm_default_values(struct hda_codec *codec, >+ struct hda_pcm_stream *info) >+{ >+ /* query support PCM information from the given NID */ >+ if (info->nid && (!info->rates || !info->formats)) { >+ snd_hda_query_supported_pcm(codec, info->nid, >+ info->rates ? NULL : &info->rates, >+ info->formats ? NULL : &info->formats, >+ info->maxbps ? NULL : &info->maxbps); >+ } >+ 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) { >+ snd_assert(info->nid, return -EINVAL); >+ info->ops.prepare = hda_pcm_default_prepare; >+ } >+ if (info->ops.cleanup == NULL) { >+ snd_assert(info->nid, return -EINVAL); >+ info->ops.cleanup = hda_pcm_default_cleanup; >+ } >+ 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 successfull, 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) { >+ unsigned int pcm, s; >+ int err; >+ if (!codec->patch_ops.build_pcms) >+ continue; >+ err = codec->patch_ops.build_pcms(codec); >+ if (err < 0) >+ return err; >+ for (pcm = 0; pcm < codec->num_pcms; pcm++) { >+ for (s = 0; s < 2; s++) { >+ struct hda_pcm_stream *info; >+ info = &codec->pcm_info[pcm].stream[s]; >+ if (!info->substreams) >+ continue; >+ err = set_pcm_default_values(codec, info); >+ if (err < 0) >+ return err; >+ } >+ } >+ } >+ return 0; >+} >+ >+/** >+ * 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 **models, >+ const struct snd_pci_quirk *tbl) >+{ >+ if (codec->bus->modelname && models) { >+ int i; >+ for (i = 0; i < num_configs; i++) { >+ if (models[i] && >+ !strcmp(codec->bus->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; >+} >+ >+/** >+ * 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, struct snd_kcontrol_new *knew) >+{ >+ int err; >+ >+ for (; knew->name; knew++) { >+ struct snd_kcontrol *kctl; >+ kctl = snd_ctl_new1(knew, codec); >+ if (!kctl) >+ return -ENOMEM; >+ err = snd_ctl_add(codec->bus->card, kctl); >+ if (err < 0) { >+ if (!codec->addr) >+ return err; >+ kctl = snd_ctl_new1(knew, codec); >+ if (!kctl) >+ return -ENOMEM; >+ kctl->id.device = codec->addr; >+ err = snd_ctl_add(codec->bus->card, kctl); >+ if (err < 0) >+ return err; >+ } >+ } >+ return 0; >+} >+ >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, >+ unsigned int power_state); >+ >+static void hda_power_work(void *data) >+{ >+ struct hda_codec *codec = data; >+ >+ if (!codec->power_on || codec->power_count) { >+ codec->power_transition = 0; >+ return; >+ } >+ >+ hda_call_codec_suspend(codec); >+ if (codec->bus->ops.pm_notify) >+ codec->bus->ops.pm_notify(codec); >+} >+ >+static void hda_keep_power_on(struct hda_codec *codec) >+{ >+ codec->power_count++; >+ codec->power_on = 1; >+} >+ >+void snd_hda_power_up(struct hda_codec *codec) >+{ >+ codec->power_count++; >+ if (codec->power_on || codec->power_transition) >+ return; >+ >+ codec->power_on = 1; >+ if (codec->bus->ops.pm_notify) >+ codec->bus->ops.pm_notify(codec); >+ hda_call_codec_resume(codec); >+ cancel_delayed_work(&codec->power_work); >+ codec->power_transition = 0; >+} >+ >+void snd_hda_power_down(struct hda_codec *codec) >+{ >+ --codec->power_count; >+ if (!codec->power_on || codec->power_count || codec->power_transition) >+ return; >+ if (power_save) { >+ codec->power_transition = 1; /* avoid reentrance */ >+ schedule_delayed_work(&codec->power_work, >+ msecs_to_jiffies(power_save * 1000)); >+ } >+} >+ >+int snd_hda_check_amp_list_power(struct hda_codec *codec, >+ struct hda_loopback_check *check, >+ hda_nid_t nid) >+{ >+ 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; >+} >+#endif >+ >+/* >+ * Channel mode helper >+ */ >+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; >+} >+ >+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; >+} >+ >+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; >+} >+ >+/* >+ * input MUX helper >+ */ >+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; >+} >+ >+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; >+} >+ >+ >+/* >+ * 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) >+{ >+ /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */ >+ if (codec->spdif_ctls & AC_DIG1_ENABLE) >+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, >+ codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff); >+ snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format); >+ /* turn on again (if needed) */ >+ if (codec->spdif_ctls & AC_DIG1_ENABLE) >+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, >+ codec->spdif_ctls & 0xff); >+} >+ >+/* >+ * 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 */ >+ snd_hda_codec_cleanup_stream(codec, mout->dig_out_nid); >+ mout->dig_out_used = HDA_DIG_EXCLUSIVE; >+ mutex_unlock(&codec->spdif_mutex); >+ return 0; >+} >+ >+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; >+} >+ >+/* >+ * release the digital out >+ */ >+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; >+} >+ >+/* >+ * set up more restrictions for analog out >+ */ >+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) { >+ runtime->hw.rates &= mout->spdif_rates; >+ runtime->hw.formats &= mout->spdif_formats; >+ if (mout->spdif_maxbps < hinfo->maxbps) >+ hinfo->maxbps = mout->spdif_maxbps; >+ } >+ mutex_unlock(&codec->spdif_mutex); >+ } >+ return snd_pcm_hw_constraint_step(substream->runtime, 0, >+ SNDRV_PCM_HW_PARAM_CHANNELS, 2); >+} >+ >+/* >+ * 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) >+{ >+ hda_nid_t *nids = mout->dac_nids; >+ int chs = substream->runtime->channels; >+ 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) && >+ !(codec->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; >+ snd_hda_codec_cleanup_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->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; >+} >+ >+/* >+ * clean up the setting for analog out >+ */ >+int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, >+ struct hda_multi_out *mout) >+{ >+ 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->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) { >+ snd_hda_codec_cleanup_stream(codec, mout->dig_out_nid); >+ mout->dig_out_used = 0; >+ } >+ mutex_unlock(&codec->spdif_mutex); >+ return 0; >+} >+ >+/* >+ * Helper for automatic ping configuration >+ */ >+ >+static int is_in_nid_list(hda_nid_t nid, 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; >+ } >+ } >+ } >+} >+ >+ >+/* >+ * 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 input_pins array. >+ * The digital input/output pins are assigned to dig_in_pin and dig_out_pin, >+ * respectively. >+ */ >+int snd_hda_parse_pin_def_config(struct hda_codec *codec, >+ struct auto_pin_cfg *cfg, >+ hda_nid_t *ignore_nids) >+{ >+ hda_nid_t nid, end_nid; >+ short seq, assoc_line_out, assoc_speaker; >+ 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)]; >+ >+ 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 = assoc_speaker = 0; >+ >+ end_nid = codec->start_nid + codec->num_nodes; >+ for (nid = codec->start_nid; nid < end_nid; nid++) { >+ unsigned int wid_caps = get_wcaps(codec, nid); >+ unsigned int wid_type = >+ (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; >+ unsigned int def_conf; >+ short assoc, loc; >+ >+ /* 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_read(codec, nid, 0, >+ AC_VERB_GET_CONFIG_DEFAULT, 0); >+ if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) >+ continue; >+ loc = get_defcfg_location(def_conf); >+ switch (get_defcfg_device(def_conf)) { >+ 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 (! assoc) >+ continue; >+ if (! assoc_speaker) >+ assoc_speaker = assoc; >+ else if (assoc_speaker != assoc) >+ continue; >+ if (cfg->speaker_outs >= ARRAY_SIZE(cfg->speaker_pins)) >+ continue; >+ cfg->speaker_pins[cfg->speaker_outs] = nid; >+ sequences_speaker[cfg->speaker_outs] = 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: { >+ int preferred, alt; >+ if (loc == AC_JACK_LOC_FRONT) { >+ preferred = AUTO_PIN_FRONT_MIC; >+ alt = AUTO_PIN_MIC; >+ } else { >+ preferred = AUTO_PIN_MIC; >+ alt = AUTO_PIN_FRONT_MIC; >+ } >+ if (!cfg->input_pins[preferred]) >+ cfg->input_pins[preferred] = nid; >+ else if (!cfg->input_pins[alt]) >+ cfg->input_pins[alt] = nid; >+ break; >+ } >+ case AC_JACK_LINE_IN: >+ if (loc == AC_JACK_LOC_FRONT) >+ cfg->input_pins[AUTO_PIN_FRONT_LINE] = nid; >+ else >+ cfg->input_pins[AUTO_PIN_LINE] = nid; >+ break; >+ case AC_JACK_CD: >+ cfg->input_pins[AUTO_PIN_CD] = nid; >+ break; >+ case AC_JACK_AUX: >+ cfg->input_pins[AUTO_PIN_AUX] = nid; >+ break; >+ case AC_JACK_SPDIF_OUT: >+ cfg->dig_out_pin = nid; >+ break; >+ case AC_JACK_SPDIF_IN: >+ cfg->dig_in_pin = nid; >+ 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) { >+ int i = 0; >+ while (i < cfg->hp_outs) { >+ /* The real HPs should have the sequence 0x0f */ >+ if ((sequences_hp[i] & 0x0f) == 0x0f) { >+ i++; >+ continue; >+ } >+ /* Move it to the line-out table */ >+ cfg->line_out_pins[cfg->line_outs] = cfg->hp_pins[i]; >+ sequences_line_out[cfg->line_outs] = sequences_hp[i]; >+ cfg->line_outs++; >+ cfg->hp_outs--; >+ memmove(cfg->hp_pins + i, cfg->hp_pins + i + 1, >+ sizeof(cfg->hp_pins[0]) * (cfg->hp_outs - i)); >+ memmove(sequences_hp + i - 1, sequences_hp + i, >+ sizeof(sequences_hp[0]) * (cfg->hp_outs - i)); >+ } >+ } >+ >+ /* sort by sequence */ >+ sort_pins_by_sequence(cfg->line_out_pins, sequences_line_out, >+ cfg->line_outs); >+ sort_pins_by_sequence(cfg->speaker_pins, sequences_speaker, >+ cfg->speaker_outs); >+ sort_pins_by_sequence(cfg->hp_pins, sequences_hp, >+ cfg->hp_outs); >+ >+ /* if we have only one mic, make it AUTO_PIN_MIC */ >+ if (!cfg->input_pins[AUTO_PIN_MIC] && >+ cfg->input_pins[AUTO_PIN_FRONT_MIC]) { >+ cfg->input_pins[AUTO_PIN_MIC] = >+ cfg->input_pins[AUTO_PIN_FRONT_MIC]; >+ cfg->input_pins[AUTO_PIN_FRONT_MIC] = 0; >+ } >+ /* ditto for line-in */ >+ if (!cfg->input_pins[AUTO_PIN_LINE] && >+ cfg->input_pins[AUTO_PIN_FRONT_LINE]) { >+ cfg->input_pins[AUTO_PIN_LINE] = >+ cfg->input_pins[AUTO_PIN_FRONT_LINE]; >+ cfg->input_pins[AUTO_PIN_FRONT_LINE] = 0; >+ } >+ >+ /* >+ * FIX-UP: if no line-outs are detected, try to use speaker or HP pin >+ * as a primary output >+ */ >+ if (!cfg->line_outs) { >+ 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 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 >+ */ >+ switch (cfg->line_outs) { >+ case 3: >+ case 4: >+ nid = cfg->line_out_pins[1]; >+ cfg->line_out_pins[1] = cfg->line_out_pins[2]; >+ cfg->line_out_pins[2] = nid; >+ break; >+ } >+ >+ /* >+ * debug prints of the parsed results >+ */ >+ snd_printd("autoconfig: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\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]); >+ 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); >+ snd_printd(" inputs: mic=0x%x, fmic=0x%x, line=0x%x, fline=0x%x," >+ " cd=0x%x, aux=0x%x\n", >+ cfg->input_pins[AUTO_PIN_MIC], >+ cfg->input_pins[AUTO_PIN_FRONT_MIC], >+ cfg->input_pins[AUTO_PIN_LINE], >+ cfg->input_pins[AUTO_PIN_FRONT_LINE], >+ cfg->input_pins[AUTO_PIN_CD], >+ cfg->input_pins[AUTO_PIN_AUX]); >+ >+ return 0; >+} >+ >+/* labels for input pins */ >+const char *auto_pin_cfg_labels[AUTO_PIN_LAST] = { >+ "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux" >+}; >+ >+ >+#ifdef CONFIG_PM >+/* >+ * power management >+ */ >+ >+/** >+ * snd_hda_suspend - suspend the codecs >+ * @bus: the HDA bus >+ * @state: suspsend state >+ * >+ * Returns 0 if successful. >+ */ >+int snd_hda_suspend(struct hda_bus *bus, pm_message_t state) >+{ >+ struct hda_codec *codec; >+ >+ list_for_each_entry(codec, &bus->codec_list, list) { >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+ if (!codec->power_on) >+ continue; >+#endif >+ hda_call_codec_suspend(codec); >+ } >+ return 0; >+} >+ >+/** >+ * snd_hda_resume - resume the codecs >+ * @bus: the HDA bus >+ * @state: resume state >+ * >+ * Returns 0 if successful. >+ * >+ * This fucntion 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 (snd_hda_codec_needs_resume(codec)) >+ hda_call_codec_resume(codec); >+ } >+ return 0; >+} >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+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; >+} >+#endif >+#endif >diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h >new file mode 100644 >index 0000000..9f523fa >--- /dev/null >+++ b/sound/pci/hda/hda_codec.h >@@ -0,0 +1,763 @@ >+/* >+ * 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> >+ >+#if defined(CONFIG_PM) || defined(CONFIG_SND_HDA_POWER_SAVE) >+#define SND_HDA_NEEDS_RESUME /* resume control code is required */ >+#endif >+ >+/* >+ * 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 >+#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 >+ >+/* >+ * 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_CODEC_RESET 0x7ff >+ >+/* >+ * 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 >+ >+/* >+ * 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_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) >+ >+/* Unsolicited response */ >+#define AC_UNSOL_TAG (0x3f<<0) >+#define AC_UNSOL_ENABLED (1<<7) >+ >+/* 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 */ >+#define AC_PINCAP_VREF (0x37<<8) >+#define AC_PINCAP_VREF_SHIFT 8 >+#define AC_PINCAP_EAPD (1<<16) /* EAPD capable */ >+/* 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 >+ >+/* 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) >+ >+/* 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) >+ >+/* >+ * 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_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) >+ >+/* Unsolicited response - 8bit */ >+#define AC_USRSP_EN (1<<7) >+ >+/* Pin sense - 32bit */ >+#define AC_PINSENSE_IMPEDANCE_MASK (0x7fffffff) >+#define AC_PINSENSE_PRESENCE (1<<31) >+ >+/* EAPD/BTL enable - 32bit */ >+#define AC_EAPDBTL_BALANCED (1<<0) >+#define AC_EAPDBTL_EAPD (1<<1) >+#define AC_EAPDBTL_LR_SWAP (1<<2) >+ >+/* 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 >+ >+/* >+ * Structures >+ */ >+ >+struct hda_bus; >+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_codec *codec, hda_nid_t nid, int direct, >+ unsigned int verb, unsigned int parm); >+ /* get a response from the last command */ >+ unsigned int (*get_response)(struct hda_codec *codec); >+ /* free the private data */ >+ void (*private_free)(struct hda_bus *); >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+ /* notify power-up/down from codec to controller */ >+ void (*pm_notify)(struct hda_codec *codec); >+#endif >+}; >+ >+/* template to pass to the bus constructor */ >+struct hda_bus_template { >+ void *private_data; >+ struct pci_dev *pci; >+ const char *modelname; >+ 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; >+ 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; >+ >+ /* unsolicited event queue */ >+ struct hda_bus_unsolicited *unsol; >+ >+ struct snd_info_entry *proc; >+ >+ /* misc op flags */ >+ unsigned int needs_damn_long_delay :1; >+}; >+ >+/* >+ * 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); >+}; >+ >+/* 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); >+#ifdef SND_HDA_NEEDS_RESUME >+ int (*suspend)(struct hda_codec *codec, pm_message_t state); >+ 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 >+}; >+ >+/* 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 */ >+ unsigned int num_entries; /* number of assigned entries */ >+ unsigned int size; /* allocated size */ >+ unsigned int record_size; /* record size (including header) */ >+ void *buffer; /* hash table entries */ >+}; >+ >+/* 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; /* assigned device number */ >+}; >+ >+/* 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 */ >+ u32 vendor_id; >+ u32 subsystem_id; >+ u32 revision_id; >+ >+ /* detected preset */ >+ const struct hda_codec_preset *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; >+ >+ /* widget capabilities cache */ >+ unsigned int num_nodes; >+ hda_nid_t start_nid; >+ u32 *wcaps; >+ >+ struct hda_cache_rec amp_cache; /* cache for amp access */ >+ struct hda_cache_rec cmd_cache; /* cache for other commands */ >+ >+ struct mutex spdif_mutex; >+ unsigned int spdif_status; /* IEC958 status bits */ >+ unsigned short spdif_ctls; /* SPDIF control bits */ >+ unsigned int spdif_in_enable; /* SPDIF input enable? */ >+ >+ struct snd_hwdep *hwdep; /* assigned hwdep device */ >+ >+#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 work_struct power_work; /* delayed task for powerdown */ >+#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); >+ >+/* >+ * 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); >+ >+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 SND_HDA_NEEDS_RESUME >+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); >+void snd_hda_codec_resume_cache(struct hda_codec *codec); >+#else >+#define snd_hda_codec_write_cache snd_hda_codec_write >+#define snd_hda_sequence_write_cache snd_hda_sequence_write >+#endif >+ >+/* >+ * Mixer >+ */ >+int snd_hda_build_controls(struct hda_bus *bus); >+ >+/* >+ * PCM >+ */ >+int snd_hda_build_pcms(struct hda_bus *bus); >+void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, >+ u32 stream_tag, >+ int channel_id, int format); >+void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid); >+unsigned int snd_hda_calc_stream_format(unsigned int rate, >+ unsigned int channels, >+ unsigned int format, >+ unsigned int maxbps); >+int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, >+ u32 *ratesp, u64 *formatsp, unsigned int *bpsp); >+int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid, >+ unsigned int format); >+ >+/* >+ * Misc >+ */ >+void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen); >+ >+/* >+ * power management >+ */ >+#ifdef CONFIG_PM >+int snd_hda_suspend(struct hda_bus *bus, pm_message_t state); >+int snd_hda_resume(struct hda_bus *bus); >+#endif >+ >+/* >+ * power saving >+ */ >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+void snd_hda_power_up(struct hda_codec *codec); >+void snd_hda_power_down(struct hda_codec *codec); >+#define snd_hda_codec_needs_resume(codec) codec->power_count >+int snd_hda_codecs_inuse(struct hda_bus *bus); >+#else >+static inline void snd_hda_power_up(struct hda_codec *codec) {} >+static inline void snd_hda_power_down(struct hda_codec *codec) {} >+#define snd_hda_codec_needs_resume(codec) 1 >+#define snd_hda_codecs_inuse(bus) 1 >+#endif >+ >+#endif /* __SOUND_HDA_CODEC_H */ >diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c >new file mode 100644 >index 0000000..b2908b8 >--- /dev/null >+++ b/sound/pci/hda/hda_generic.c >@@ -0,0 +1,1099 @@ >+/* >+ * 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 <sound/driver.h> >+#include <linux/init.h> >+#include <linux/slab.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; >+ char cap_labels[HDA_MAX_NUM_INPUTS][16]; >+ >+ 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; >+ nconns = snd_hda_get_connections(codec, nid, conn_list, >+ HDA_MAX_CONNECTIONS); >+ if (nconns < 0) { >+ kfree(node); >+ return nconns; >+ } >+ 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; >+ node->wid_caps = get_wcaps(codec, nid); >+ node->type = (node->wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; >+ >+ if (node->type == AC_WID_PIN) { >+ node->pin_caps = snd_hda_param_read(codec, node->nid, AC_PAR_PIN_CAP); >+ node->pin_ctl = snd_hda_codec_read(codec, node->nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); >+ node->def_cfg = snd_hda_codec_read(codec, node->nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); >+ } >+ >+ 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; >+ >+ snd_assert(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 i, err; >+ unsigned int pinctl; >+ char *label; >+ 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); >+ 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"; >+ } >+ label = spec->cap_labels[spec->input_mux.num_items]; >+ strcpy(label, type); >+ spec->input_mux.items[spec->input_mux.num_items].label = label; >+ >+ /* 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 */ >+} >+ >+/* add a capture source element */ >+static void add_cap_src(struct hda_gspec *spec, int idx) >+{ >+ struct hda_input_mux_item *csrc; >+ char *buf; >+ int num, ocap; >+ >+ num = spec->input_mux.num_items; >+ csrc = &spec->input_mux.items[num]; >+ buf = spec->cap_labels[num]; >+ for (ocap = 0; ocap < num; ocap++) { >+ if (! strcmp(buf, spec->cap_labels[ocap])) { >+ /* same label already exists, >+ * put the index number to be unique >+ */ >+ sprintf(buf, "%s %d", spec->cap_labels[ocap], num); >+ break; >+ } >+ } >+ csrc->index = idx; >+ spec->input_mux.num_items++; >+} >+ >+/* >+ * 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); >+ if (err < 0) >+ return err; >+ else if (err > 0) >+ add_cap_src(spec, i); >+ } >+ } >+ /* ... 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); >+ if (err < 0) >+ return err; >+ else if (err > 0) >+ add_cap_src(spec, i); >+ } >+ } >+ >+ 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); >+ if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 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); >+ if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 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); >+ if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0) >+ return err; >+ created = 1; >+ } else if ((node->wid_caps & AC_WCAP_OUT_AMP) && >+ (node->amp_out_caps & AC_AMPCAP_NUM_STEPS)) { >+ knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, 0, HDA_OUTPUT); >+ snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid); >+ if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0) >+ 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 **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 *types_speaker[] = { "Speaker", "Headphone" }; >+ static const char *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 */ >+ if ((err = snd_ctl_add(codec->bus->card, >+ snd_ctl_new1(&cap_sel, codec))) < 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); >+ if ((err = snd_ctl_add(codec->bus->card, >+ snd_ctl_new1(&knew, codec))) < 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; >+} >diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c >new file mode 100644 >index 0000000..dba1869 >--- /dev/null >+++ b/sound/pci/hda/hda_hwdep.c >@@ -0,0 +1,122 @@ >+/* >+ * 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 <sound/driver.h> >+#include <linux/init.h> >+#include <linux/slab.h> >+#include <linux/pci.h> >+#include <linux/compat.h> >+#include <linux/mutex.h> >+#include <sound/core.h> >+#include "hda_codec.h" >+#include "hda_local.h" >+#include <sound/hda_hwdep.h> >+ >+/* >+ * 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; >+} >+ >+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->exclusive = 1; >+ >+ hwdep->ops.open = hda_hwdep_open; >+ hwdep->ops.ioctl = hda_hwdep_ioctl; >+#if 0 // def CONFIG_COMPAT >+ hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat; >+#endif >+ >+ return 0; >+} >diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c >new file mode 100644 >index 0000000..c00e5b7 >--- /dev/null >+++ b/sound/pci/hda/hda_intel.c >@@ -0,0 +1,2271 @@ >+/* >+ * >+ * 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 <sound/driver.h> >+#include <asm/io.h> >+#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 <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 int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; >+static char *model[SNDRV_CARDS]; >+static int position_fix[SNDRV_CARDS]; >+static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1}; >+static int single_cmd; >+static int enable_msi; >+ >+module_param_array1(index, int, NULL, 0444); >+MODULE_PARM_DESC(index, "Index value for Intel HD audio interface."); >+module_param_array1(id, charp, NULL, 0444); >+MODULE_PARM_DESC(id, "ID string for Intel HD audio interface."); >+module_param_array1(enable, bool, NULL, 0444); >+MODULE_PARM_DESC(enable, "Enable Intel HD audio interface."); >+module_param_array1(model, charp, NULL, 0444); >+MODULE_PARM_DESC(model, "Use the given board model."); >+module_param_array1(position_fix, int, NULL, 0444); >+MODULE_PARM_DESC(position_fix, "Fix DMA pointer " >+ "(0 = auto, 1 = none, 2 = POSBUF, 3 = FIFO size)."); >+module_param_array1(probe_mask, int, NULL, 0444); >+MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1)."); >+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, int, 0444); >+MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)"); >+ >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+/* power_save option is defined in hda_codec.c */ >+ >+/* reset the HD-audio controller in power save mode. >+ * this may give more power-saving, but will take longer time to >+ * wake up. >+ */ >+static int power_save_controller = 1; >+module_param(power_save_controller, bool, 0644); >+MODULE_PARM_DESC(power_save_controller, "Reset controller in power save mode."); >+#endif >+ >+MODULE_LICENSE("GPL"); >+MODULE_SUPPORTED_DEVICE("{{Intel, ICH6}," >+ "{Intel, ICH6M}," >+ "{Intel, ICH7}," >+ "{Intel, ESB2}," >+ "{Intel, ICH8}," >+ "{Intel, ICH9}," >+ "{Intel, ICH10}," >+ "{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"); >+ >+#define SFX "hda-intel: " >+ >+ >+/* >+ * registers >+ */ >+#define ICH6_REG_GCAP 0x00 >+#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_REG_WAKEEN 0x0c >+#define ICH6_REG_STATESTS 0x0e >+#define ICH6_REG_GSTS 0x10 >+#define ICH6_REG_INTCTL 0x20 >+#define ICH6_REG_INTSTS 0x24 >+#define ICH6_REG_WALCLK 0x30 >+#define ICH6_REG_SYNC 0x34 >+#define ICH6_REG_CORBLBASE 0x40 >+#define ICH6_REG_CORBUBASE 0x44 >+#define ICH6_REG_CORBWP 0x48 >+#define ICH6_REG_CORBRP 0x4A >+#define ICH6_REG_CORBCTL 0x4c >+#define ICH6_REG_CORBSTS 0x4d >+#define ICH6_REG_CORBSIZE 0x4e >+ >+#define ICH6_REG_RIRBLBASE 0x50 >+#define ICH6_REG_RIRBUBASE 0x54 >+#define ICH6_REG_RIRBWP 0x58 >+#define ICH6_REG_RINTCNT 0x5a >+#define ICH6_REG_RIRBCTL 0x5c >+#define ICH6_REG_RIRBSTS 0x5d >+#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) >+/* max number of PCM devics per card */ >+#define AZX_MAX_PCMS 8 >+ >+/* 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: SD2,SD1,SD0 */ >+#define AZX_MAX_CODECS 3 >+#define STATESTS_INT_MASK 0x07 >+ >+/* 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 */ >+ >+/* GCTL unsolicited response enable bit */ >+#define ICH6_GCTL_UREN (1<<8) >+ >+/* GCTL reset bit */ >+#define ICH6_GCTL_RESET (1<<0) >+ >+/* CORB/RIRB control, read/write pointer */ >+#define ICH6_RBCTL_DMA_EN 0x02 /* enable DMA */ >+#define ICH6_RBCTL_IRQ_EN 0x01 /* enable IRQ */ >+#define ICH6_RBRWP_CLR 0x8000 /* read/write pointer clear */ >+/* 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_NONE, >+ POS_FIX_POSBUF, >+ POS_FIX_FIFO, >+}; >+ >+/* 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 >+ >+/* Defines for Intel SCH HDA snoop control */ >+#define INTEL_SCH_HDA_DEVC 0x78 >+#define INTEL_SCH_HDA_DEVC_NOSNOOP (0x1<<11) >+ >+ >+/* >+ */ >+ >+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 */ >+ >+ 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 */ >+ >+ unsigned int opened :1; >+ unsigned int running :1; >+ unsigned int irq_pending: 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; /* number of pending requests */ >+ u32 res; /* last read value */ >+}; >+ >+struct azx { >+ struct snd_card *card; >+ struct pci_dev *pci; >+ >+ /* chip type specific */ >+ int driver_type; >+ 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 snd_pcm *pcm[AZX_MAX_PCMS]; >+ >+ /* HD codec */ >+ unsigned short codec_mask; >+ struct hda_bus *bus; >+ >+ /* 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; >+ unsigned int running :1; >+ unsigned int initialized :1; >+ unsigned int single_cmd :1; >+ unsigned int polling_mode :1; >+ unsigned int msi :1; >+ >+ /* for debugging */ >+ unsigned int last_cmd; /* last issued command (to sync) */ >+ >+ /* for pending irqs */ >+ struct work_struct irq_pending_work; >+}; >+ >+/* driver types */ >+enum { >+ AZX_DRIVER_ICH, >+ AZX_DRIVER_SCH, >+ AZX_DRIVER_ATI, >+ AZX_DRIVER_ATIHDMI, >+ AZX_DRIVER_VIA, >+ AZX_DRIVER_SIS, >+ AZX_DRIVER_ULI, >+ AZX_DRIVER_NVIDIA, >+ AZX_DRIVER_TERA, >+}; >+ >+static char *driver_short_names[] __devinitdata = { >+ [AZX_DRIVER_ICH] = "HDA Intel", >+ [AZX_DRIVER_SCH] = "HDA Intel MID", >+ [AZX_DRIVER_ATI] = "HDA ATI SB", >+ [AZX_DRIVER_ATIHDMI] = "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", >+}; >+ >+/* >+ * 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) >+ >+/* Get the upper 32bit of the given dma_addr_t >+ * Compiler should optimize and eliminate the code if dma_addr_t is 32bit >+ */ >+#define upper_32bit(addr) (sizeof(addr) > 4 ? (u32)((addr) >> 32) : (u32)0) >+ >+static int azx_acquire_irq(struct azx *chip, int do_disconnect); >+ >+/* >+ * 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; >+ } >+ return 0; >+} >+ >+static void azx_init_cmd_io(struct azx *chip) >+{ >+ /* 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_32bit(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_RBRWP_CLR); >+ /* enable corb dma */ >+ azx_writeb(chip, CORBCTL, ICH6_RBCTL_DMA_EN); >+ >+ /* RIRB set up */ >+ chip->rirb.addr = chip->rb.addr + 2048; >+ chip->rirb.buf = (u32 *)(chip->rb.area + 2048); >+ azx_writel(chip, RIRBLBASE, (u32)chip->rirb.addr); >+ azx_writel(chip, RIRBUBASE, upper_32bit(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_RBRWP_CLR); >+ /* set N=1, get RIRB response interrupt for new entry */ >+ azx_writew(chip, RINTCNT, 1); >+ /* enable rirb dma and response irq */ >+ azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN | ICH6_RBCTL_IRQ_EN); >+ chip->rirb.rp = chip->rirb.cmds = 0; >+} >+ >+static void azx_free_cmd_io(struct azx *chip) >+{ >+ /* disable ringbuffer DMAs */ >+ azx_writeb(chip, RIRBCTL, 0); >+ azx_writeb(chip, CORBCTL, 0); >+} >+ >+/* send a command */ >+static int azx_corb_send_cmd(struct hda_codec *codec, u32 val) >+{ >+ struct azx *chip = codec->bus->private_data; >+ unsigned int wp; >+ >+ /* add command to corb */ >+ wp = azx_readb(chip, CORBWP); >+ wp++; >+ wp %= ICH6_MAX_CORB_ENTRIES; >+ >+ spin_lock_irq(&chip->reg_lock); >+ chip->rirb.cmds++; >+ 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; >+ 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]); >+ if (res_ex & ICH6_RIRB_EX_UNSOL_EV) >+ snd_hda_queue_unsol_event(chip->bus, res, res_ex); >+ else if (chip->rirb.cmds) { >+ chip->rirb.res = res; >+ smp_wmb(); >+ chip->rirb.cmds--; >+ } >+ } >+} >+ >+/* receive a response */ >+static unsigned int azx_rirb_get_response(struct hda_codec *codec) >+{ >+ struct azx *chip = codec->bus->private_data; >+ unsigned long timeout; >+ >+ again: >+ timeout = jiffies + msecs_to_jiffies(1000); >+ for (;;) { >+ if (chip->polling_mode) { >+ spin_lock_irq(&chip->reg_lock); >+ azx_update_rirb(chip); >+ spin_unlock_irq(&chip->reg_lock); >+ } >+ if (!chip->rirb.cmds) { >+ smp_rmb(); >+ return chip->rirb.res; /* the last value */ >+ } >+ if (time_after(jiffies, timeout)) >+ break; >+ if (codec->bus->needs_damn_long_delay) >+ msleep(2); /* temporary workaround */ >+ else { >+ udelay(10); >+ cond_resched(); >+ } >+ } >+ >+ if (chip->msi) { >+ snd_printk(KERN_WARNING "hda_intel: No response from codec, " >+ "disabling MSI: last cmd=0x%08x\n", chip->last_cmd); >+ free_irq(chip->irq, chip); >+ chip->irq = -1; >+ pci_disable_msi(chip->pci); >+ chip->msi = 0; >+ if (azx_acquire_irq(chip, 1) < 0) >+ return -1; >+ goto again; >+ } >+ >+ if (!chip->polling_mode) { >+ snd_printk(KERN_WARNING "hda_intel: azx_get_response timeout, " >+ "switching to polling mode: last cmd=0x%08x\n", >+ chip->last_cmd); >+ chip->polling_mode = 1; >+ goto again; >+ } >+ >+ snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, " >+ "switching to single_cmd mode: last cmd=0x%08x\n", >+ chip->last_cmd); >+ chip->rirb.rp = azx_readb(chip, RIRBWP); >+ chip->rirb.cmds = 0; >+ /* switch to single_cmd mode */ >+ chip->single_cmd = 1; >+ azx_free_cmd_io(chip); >+ 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. >+ */ >+ >+/* send a command */ >+static int azx_single_send_cmd(struct hda_codec *codec, u32 val) >+{ >+ struct azx *chip = codec->bus->private_data; >+ int timeout = 50; >+ >+ 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 0; >+ } >+ 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_codec *codec) >+{ >+ struct azx *chip = codec->bus->private_data; >+ int timeout = 50; >+ >+ while (timeout--) { >+ /* check IRV busy bit */ >+ if (azx_readw(chip, IRS) & ICH6_IRS_VALID) >+ return azx_readl(chip, IR); >+ udelay(1); >+ } >+ if (printk_ratelimit()) >+ snd_printd(SFX "get_response timeout: IRS=0x%x\n", >+ azx_readw(chip, IRS)); >+ return (unsigned int)-1; >+} >+ >+/* >+ * 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_codec *codec, hda_nid_t nid, >+ int direct, unsigned int verb, >+ unsigned int para) >+{ >+ struct azx *chip = codec->bus->private_data; >+ u32 val; >+ >+ val = (u32)(codec->addr & 0x0f) << 28; >+ val |= (u32)direct << 27; >+ val |= (u32)nid << 20; >+ val |= verb << 8; >+ val |= para; >+ chip->last_cmd = val; >+ >+ if (chip->single_cmd) >+ return azx_single_send_cmd(codec, val); >+ else >+ return azx_corb_send_cmd(codec, val); >+} >+ >+/* get a response */ >+static unsigned int azx_get_response(struct hda_codec *codec) >+{ >+ struct azx *chip = codec->bus->private_data; >+ if (chip->single_cmd) >+ return azx_single_get_response(codec); >+ else >+ return azx_rirb_get_response(codec); >+} >+ >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+static void azx_power_notify(struct hda_codec *codec); >+#endif >+ >+/* reset codec link */ >+static int azx_reset(struct azx *chip) >+{ >+ int count; >+ >+ /* 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); >+ >+ /* check to see if controller is ready */ >+ if (!azx_readb(chip, GCTL)) { >+ snd_printd("azx_reset: controller not ready!\n"); >+ return -EBUSY; >+ } >+ >+ /* Accept unsolicited responses */ >+ azx_writel(chip, GCTL, azx_readl(chip, GCTL) | ICH6_GCTL_UREN); >+ >+ /* detect codecs */ >+ if (!chip->codec_mask) { >+ chip->codec_mask = azx_readw(chip, STATESTS); >+ snd_printdd("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) >+{ >+ /* enable SIE */ >+ azx_writeb(chip, INTCTL, >+ azx_readb(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 a stream */ >+static void azx_stream_stop(struct azx *chip, struct azx_dev *azx_dev) >+{ >+ /* stop DMA */ >+ 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 */ >+ /* disable SIE */ >+ azx_writeb(chip, INTCTL, >+ azx_readb(chip, INTCTL) & ~(1 << azx_dev->index)); >+} >+ >+ >+/* >+ * reset and start the controller registers >+ */ >+static void azx_init_chip(struct azx *chip) >+{ >+ if (chip->initialized) >+ return; >+ >+ /* reset controller */ >+ azx_reset(chip); >+ >+ /* 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_32bit(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) >+{ >+ unsigned short snoop; >+ >+ /* 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 >+ */ >+ update_pci_byte(chip->pci, ICH6_PCIREG_TCSEL, 0x07, 0); >+ >+ switch (chip->driver_type) { >+ case AZX_DRIVER_ATI: >+ /* For ATI SB450 azalia HD audio, we need to enable snoop */ >+ update_pci_byte(chip->pci, >+ ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR, >+ 0x07, ATI_SB450_HDAUDIO_ENABLE_SNOOP); >+ break; >+ case AZX_DRIVER_NVIDIA: >+ /* For NVIDIA HDA, enable snoop */ >+ update_pci_byte(chip->pci, >+ NVIDIA_HDA_TRANSREG_ADDR, >+ 0x0f, NVIDIA_HDA_ENABLE_COHBITS); >+ break; >+ case AZX_DRIVER_SCH: >+ pci_read_config_word(chip->pci, INTEL_SCH_HDA_DEVC, &snoop); >+ if (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP) { >+ pci_write_config_word(chip->pci, INTEL_SCH_HDA_DEVC, \ >+ snoop & (~INTEL_SCH_HDA_DEVC_NOSNOOP)); >+ pci_read_config_word(chip->pci, >+ INTEL_SCH_HDA_DEVC, &snoop); >+ snd_printdd("HDA snoop disabled, enabling ... %s\n",\ >+ (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP) \ >+ ? "Failed" : "OK"); >+ } >+ break; >+ >+ } >+} >+ >+ >+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 pt_regs *regs) >+{ >+ struct azx *chip = dev_id; >+ struct azx_dev *azx_dev; >+ u32 status; >+ int i; >+ >+ 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) { >+ azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK); >+ if (!azx_dev->substream || !azx_dev->running) >+ continue; >+ /* check whether this IRQ is really acceptable */ >+ if (azx_position_ok(chip, azx_dev)) { >+ azx_dev->irq_pending = 0; >+ spin_unlock(&chip->reg_lock); >+ snd_pcm_period_elapsed(azx_dev->substream); >+ spin_lock(&chip->reg_lock); >+ } else { >+ /* bogus IRQ, process it later */ >+ azx_dev->irq_pending = 1; >+ schedule_work(&chip->irq_pending_work); >+ } >+ } >+ } >+ >+ /* clear rirb int */ >+ status = azx_readb(chip, RIRBSTS); >+ if (status & RIRB_INT_MASK) { >+ if (!chip->single_cmd && (status & RIRB_INT_RESPONSE)) >+ 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 BDL entries >+ */ >+static int azx_setup_periods(struct snd_pcm_substream *substream, >+ struct azx_dev *azx_dev) >+{ >+ struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream); >+ u32 *bdl; >+ int i, ofs, periods, period_bytes; >+ >+ /* reset BDL address */ >+ azx_sd_writel(azx_dev, SD_BDLPL, 0); >+ azx_sd_writel(azx_dev, SD_BDLPU, 0); >+ >+ period_bytes = snd_pcm_lib_period_bytes(substream); >+ azx_dev->period_bytes = 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; >+ for (i = 0; i < periods; i++) { >+ int size, rest; >+ if (i >= AZX_MAX_BDL_ENTRIES) { >+ snd_printk(KERN_ERR "Too many BDL entries: " >+ "buffer=%d, period=%d\n", >+ azx_dev->bufsize, period_bytes); >+ /* reset */ >+ azx_sd_writel(azx_dev, SD_BDLPL, 0); >+ azx_sd_writel(azx_dev, SD_BDLPU, 0); >+ return -EINVAL; >+ } >+ rest = period_bytes; >+ do { >+ dma_addr_t addr = snd_pcm_sgbuf_get_addr(sgbuf, ofs); >+ /* program the address field of the BDL entry */ >+ bdl[0] = cpu_to_le32((u32)addr); >+ bdl[1] = cpu_to_le32(upper_32bit(addr)); >+ /* program the size field of the BDL entry */ >+ size = PAGE_SIZE - (ofs % PAGE_SIZE); >+ if (rest < size) >+ size = rest; >+ bdl[2] = cpu_to_le32(size); >+ /* program the IOC to enable interrupt >+ * only when the whole fragment is processed >+ */ >+ rest -= size; >+ bdl[3] = rest ? 0 : cpu_to_le32(0x01); >+ bdl += 4; >+ azx_dev->frags++; >+ ofs += size; >+ } while (rest > 0); >+ } >+ return 0; >+} >+ >+/* >+ * set up the SD for streaming >+ */ >+static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev) >+{ >+ unsigned char val; >+ int timeout; >+ >+ /* make sure the run bit is zero for SD */ >+ azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) & >+ ~SD_CTL_DMA_START); >+ /* reset stream */ >+ 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) >+ ; >+ >+ /* program the stream_tag */ >+ azx_sd_writel(azx_dev, SD_CTL, >+ (azx_sd_readl(azx_dev, SD_CTL) & ~SD_CTL_STREAM_TAG_MASK)| >+ (azx_dev->stream_tag << SD_CTL_STREAM_TAG_SHIFT)); >+ >+ /* 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_32bit(azx_dev->bdl.addr)); >+ >+ /* enable the position buffer */ >+ if (chip->position_fix == POS_FIX_POSBUF || >+ chip->position_fix == POS_FIX_AUTO) { >+ if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE)) >+ azx_writel(chip, DPLBASE, >+ (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE); >+ } >+ >+ /* set the interrupt enable bits in the descriptor control register */ >+ azx_sd_writel(azx_dev, SD_CTL, >+ azx_sd_readl(azx_dev, SD_CTL) | SD_INT_MASK); >+ >+ return 0; >+} >+ >+ >+/* >+ * Codec initialization >+ */ >+ >+static unsigned int azx_max_codecs[] __devinitdata = { >+ [AZX_DRIVER_ICH] = 4, /* Some ICH9 boards use SD3 */ >+ [AZX_DRIVER_SCH] = 3, >+ [AZX_DRIVER_ATI] = 4, >+ [AZX_DRIVER_ATIHDMI] = 4, >+ [AZX_DRIVER_VIA] = 3, /* FIXME: correct? */ >+ [AZX_DRIVER_SIS] = 3, /* FIXME: correct? */ >+ [AZX_DRIVER_ULI] = 3, /* FIXME: correct? */ >+ [AZX_DRIVER_NVIDIA] = 3, /* FIXME: correct? */ >+ [AZX_DRIVER_TERA] = 1, >+}; >+ >+static int __devinit azx_codec_create(struct azx *chip, const char *model, >+ unsigned int codec_probe_mask) >+{ >+ struct hda_bus_template bus_temp; >+ int c, codecs, audio_codecs, err; >+ >+ 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; >+#ifdef CONFIG_SND_HDA_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; >+ >+ codecs = audio_codecs = 0; >+ for (c = 0; c < AZX_MAX_CODECS; c++) { >+ if ((chip->codec_mask & (1 << c)) & codec_probe_mask) { >+ struct hda_codec *codec; >+ err = snd_hda_codec_new(chip->bus, c, &codec); >+ if (err < 0) >+ continue; >+ codecs++; >+ if (codec->afg) >+ audio_codecs++; >+ } >+ } >+ if (!audio_codecs) { >+ /* probe additional slots if no codec is found */ >+ for (; c < azx_max_codecs[chip->driver_type]; c++) { >+ if ((chip->codec_mask & (1 << c)) & codec_probe_mask) { >+ err = snd_hda_codec_new(chip->bus, c, NULL); >+ if (err < 0) >+ continue; >+ codecs++; >+ } >+ } >+ } >+ if (!codecs) { >+ snd_printk(KERN_ERR SFX "no codecs initialized\n"); >+ return -ENXIO; >+ } >+ >+ return 0; >+} >+ >+ >+/* >+ * PCM support >+ */ >+ >+/* assign a stream for the PCM */ >+static inline struct azx_dev *azx_assign_device(struct azx *chip, int stream) >+{ >+ int dev, i, nums; >+ if (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) { >+ chip->azx_dev[dev].opened = 1; >+ return &chip->azx_dev[dev]; >+ } >+ return NULL; >+} >+ >+/* 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), >+ .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, >+}; >+ >+struct azx_pcm { >+ struct azx *chip; >+ struct hda_codec *codec; >+ struct hda_pcm_stream *hinfo[2]; >+}; >+ >+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; >+ >+ mutex_lock(&chip->open_mutex); >+ azx_dev = azx_assign_device(chip, substream->stream); >+ 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); >+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, >+ 128); >+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, >+ 128); >+ 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; >+ } >+ 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) >+{ >+ return snd_pcm_lib_malloc_pages(substream, >+ params_buffer_bytes(hw_params)); >+} >+ >+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 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); >+ >+ hinfo->ops.cleanup(hinfo, apcm->codec, substream); >+ >+ 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; >+ >+ azx_dev->bufsize = snd_pcm_lib_buffer_bytes(substream); >+ azx_dev->format_val = snd_hda_calc_stream_format(runtime->rate, >+ runtime->channels, >+ runtime->format, >+ hinfo->maxbps); >+ if (!azx_dev->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; >+ } >+ >+ snd_printdd("azx_pcm_prepare: bufsize=0x%x, format=0x%x\n", >+ azx_dev->bufsize, azx_dev->format_val); >+ if (azx_setup_periods(substream, azx_dev) < 0) >+ return -EINVAL; >+ azx_setup_controller(chip, azx_dev); >+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) >+ azx_dev->fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE) + 1; >+ else >+ azx_dev->fifo_size = 0; >+ >+ return hinfo->ops.prepare(hinfo, apcm->codec, azx_dev->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 start, nsync = 0, sbits = 0; >+ int nwait, timeout; >+ >+ switch (cmd) { >+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: >+ case SNDRV_PCM_TRIGGER_RESUME: >+ case SNDRV_PCM_TRIGGER_START: >+ 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 */ >+ azx_writel(chip, SYNC, azx_readl(chip, SYNC) | sbits); >+ } >+ snd_pcm_group_for_each_entry(s, substream) { >+ if (s->pcm->card != substream->pcm->card) >+ continue; >+ azx_dev = get_azx_dev(s); >+ if (start) >+ azx_stream_start(chip, azx_dev); >+ else >+ azx_stream_stop(chip, azx_dev); >+ azx_dev->running = start; >+ } >+ spin_unlock(&chip->reg_lock); >+ if (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 */ >+ azx_writel(chip, SYNC, azx_readl(chip, SYNC) & ~sbits); >+ spin_unlock(&chip->reg_lock); >+ } >+ return 0; >+} >+ >+static unsigned int azx_get_position(struct azx *chip, >+ struct azx_dev *azx_dev) >+{ >+ unsigned int pos; >+ >+ if (chip->position_fix == POS_FIX_POSBUF || >+ chip->position_fix == POS_FIX_AUTO) { >+ /* use the position buffer */ >+ pos = le32_to_cpu(*azx_dev->posbuf); >+ } else { >+ /* read LPIB */ >+ pos = azx_sd_readl(azx_dev, SD_LPIB); >+ if (chip->position_fix == POS_FIX_FIFO) >+ pos += azx_dev->fifo_size; >+ } >+ 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)); >+} >+ >+/* >+ * 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) >+{ >+ unsigned int pos; >+ >+ pos = azx_get_position(chip, azx_dev); >+ if (chip->position_fix == POS_FIX_AUTO) { >+ if (!pos) { >+ printk(KERN_WARNING >+ "hda-intel: Invalid position buffer, " >+ "using LPIB read method instead.\n"); >+ chip->position_fix = POS_FIX_NONE; >+ pos = azx_get_position(chip, azx_dev); >+ } else >+ chip->position_fix = POS_FIX_POSBUF; >+ } >+ >+ if (pos % azx_dev->period_bytes > azx_dev->period_bytes / 2) >+ return 0; /* NG - it's below the period boundary */ >+ return 1; /* OK, it's fine */ >+} >+ >+/* >+ * The work for pending PCM period updates. >+ */ >+static void azx_irq_pending_work(void *data) >+{ >+ struct work_struct *work = data; >+ struct azx *chip = container_of(work, struct azx, irq_pending_work); >+ int i, pending; >+ >+ 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; >+ if (azx_position_ok(chip, azx_dev)) { >+ azx_dev->irq_pending = 0; >+ spin_unlock(&chip->reg_lock); >+ snd_pcm_period_elapsed(azx_dev->substream); >+ spin_lock(&chip->reg_lock); >+ } else >+ pending++; >+ } >+ spin_unlock_irq(&chip->reg_lock); >+ if (!pending) >+ return; >+ cond_resched(); >+ } >+} >+ >+/* 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); >+ flush_scheduled_work(); >+} >+ >+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, >+ .page = snd_pcm_sgbuf_ops_page, >+}; >+ >+static void azx_pcm_free(struct snd_pcm *pcm) >+{ >+ kfree(pcm->private_data); >+} >+ >+static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec, >+ struct hda_pcm *cpcm) >+{ >+ int err; >+ struct snd_pcm *pcm; >+ struct azx_pcm *apcm; >+ >+ /* if no substreams are defined for both playback and capture, >+ * it's just a placeholder. ignore it. >+ */ >+ if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams) >+ return 0; >+ >+ snd_assert(cpcm->name, return -EINVAL); >+ >+ err = snd_pcm_new(chip->card, cpcm->name, cpcm->device, >+ cpcm->stream[0].substreams, >+ cpcm->stream[1].substreams, >+ &pcm); >+ if (err < 0) >+ return err; >+ strcpy(pcm->name, cpcm->name); >+ apcm = kmalloc(sizeof(*apcm), GFP_KERNEL); >+ if (apcm == NULL) >+ return -ENOMEM; >+ apcm->chip = chip; >+ apcm->codec = codec; >+ apcm->hinfo[0] = &cpcm->stream[0]; >+ apcm->hinfo[1] = &cpcm->stream[1]; >+ pcm->private_data = apcm; >+ pcm->private_free = azx_pcm_free; >+ if (cpcm->stream[0].substreams) >+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &azx_pcm_ops); >+ if (cpcm->stream[1].substreams) >+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &azx_pcm_ops); >+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, >+ snd_dma_pci_data(chip->pci), >+ 1024 * 64, 1024 * 1024); >+ chip->pcm[cpcm->device] = pcm; >+ return 0; >+} >+ >+static int __devinit azx_pcm_create(struct azx *chip) >+{ >+ static const char *dev_name[HDA_PCM_NTYPES] = { >+ "Audio", "SPDIF", "HDMI", "Modem" >+ }; >+ /* starting device index for each PCM type */ >+ static int dev_idx[HDA_PCM_NTYPES] = { >+ [HDA_PCM_TYPE_AUDIO] = 0, >+ [HDA_PCM_TYPE_SPDIF] = 1, >+ [HDA_PCM_TYPE_HDMI] = 3, >+ [HDA_PCM_TYPE_MODEM] = 6 >+ }; >+ /* normal audio device indices; not linear to keep compatibility */ >+ static int audio_idx[4] = { 0, 2, 4, 5 }; >+ struct hda_codec *codec; >+ int c, err; >+ int num_devs[HDA_PCM_NTYPES]; >+ >+ err = snd_hda_build_pcms(chip->bus); >+ if (err < 0) >+ return err; >+ >+ /* create audio PCMs */ >+ memset(num_devs, 0, sizeof(num_devs)); >+ list_for_each_entry(codec, &chip->bus->codec_list, list) { >+ for (c = 0; c < codec->num_pcms; c++) { >+ struct hda_pcm *cpcm = &codec->pcm_info[c]; >+ int type = cpcm->pcm_type; >+ switch (type) { >+ case HDA_PCM_TYPE_AUDIO: >+ if (num_devs[type] >= ARRAY_SIZE(audio_idx)) { >+ snd_printk(KERN_WARNING >+ "Too many audio devices\n"); >+ continue; >+ } >+ cpcm->device = audio_idx[num_devs[type]]; >+ break; >+ case HDA_PCM_TYPE_SPDIF: >+ case HDA_PCM_TYPE_HDMI: >+ case HDA_PCM_TYPE_MODEM: >+ if (num_devs[type]) { >+ snd_printk(KERN_WARNING >+ "%s already defined\n", >+ dev_name[type]); >+ continue; >+ } >+ cpcm->device = dev_idx[type]; >+ break; >+ default: >+ snd_printk(KERN_WARNING >+ "Invalid PCM type %d\n", type); >+ continue; >+ } >+ num_devs[type]++; >+ err = create_codec_pcm(chip, codec, cpcm); >+ if (err < 0) >+ return err; >+ } >+ } >+ return 0; >+} >+ >+/* >+ * 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, >+ "HDA Intel", 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_codec *codec) >+{ >+ struct azx *chip = codec->bus->private_data; >+ struct hda_codec *c; >+ int power_on = 0; >+ >+ list_for_each_entry(c, &codec->bus->codec_list, list) { >+ if (c->power_on) { >+ power_on = 1; >+ break; >+ } >+ } >+ if (power_on) >+ azx_init_chip(chip); >+ else if (chip->running && power_save_controller) >+ azx_stop_chip(chip); >+} >+#endif /* CONFIG_SND_HDA_POWER_SAVE */ >+ >+#ifdef CONFIG_PM >+/* >+ * power management >+ */ >+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; >+ int i; >+ >+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); >+ azx_clear_irq_pending(chip); >+ for (i = 0; i < AZX_MAX_PCMS; i++) >+ snd_pcm_suspend_all(chip->pcm[i]); >+ if (chip->initialized) >+ snd_hda_suspend(chip->bus, state); >+ azx_stop_chip(chip); >+ if (chip->irq >= 0) { >+ 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->saved_config_space); >+ 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, pci->saved_config_space); >+ 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); >+ >+ snd_hda_resume(chip->bus); >+ snd_power_change_state(card, SNDRV_CTL_POWER_D0); >+ return 0; >+} >+#endif /* CONFIG_PM */ >+ >+ >+/* >+ * destructor >+ */ >+static int azx_free(struct azx *chip) >+{ >+ int i; >+ >+ 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) >+ snd_dma_free_pages(&chip->azx_dev[i].bdl); >+ } >+ if (chip->rb.area) >+ snd_dma_free_pages(&chip->rb); >+ if (chip->posbuf.area) >+ 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_NONE), >+ SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_NONE), >+ SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_NONE), >+ {} >+}; >+ >+static int __devinit check_position_fix(struct azx *chip, int fix) >+{ >+ const struct snd_pci_quirk *q; >+ >+ if (fix == POS_FIX_AUTO) { >+ 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; >+ } >+ } >+ return fix; >+} >+ >+/* >+ * 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), >+ {} >+}; >+ >+static void __devinit check_probe_mask(struct azx *chip, int dev) >+{ >+ const struct snd_pci_quirk *q; >+ >+ if (probe_mask[dev] == -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); >+ probe_mask[dev] = q->value; >+ } >+ } >+} >+ >+ >+/* >+ * constructor >+ */ >+static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, >+ int dev, int driver_type, >+ 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_type = driver_type; >+ chip->msi = enable_msi; >+ INIT_WORK(&chip->irq_pending_work, azx_irq_pending_work, &chip->irq_pending_work); >+ >+ chip->position_fix = check_position_fix(chip, position_fix[dev]); >+ check_probe_mask(chip, dev); >+ >+ chip->single_cmd = single_cmd; >+ >+#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 = ioremap_nocache(chip->addr, pci_resource_len(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("chipset global capabilities = 0x%x\n", gcap); >+ >+ /* allow 64bit DMA address if supported by H/W */ >+ if ((gcap & 0x01) && !pci_set_dma_mask(pci, DMA_64BIT_MASK)) >+ pci_set_consistent_dma_mask(pci, DMA_64BIT_MASK); >+ >+ /* read number of streams from GCAP register instead of using >+ * hardcoded value >+ */ >+ chip->capture_streams = (gcap >> 8) & 0x0f; >+ chip->playback_streams = (gcap >> 12) & 0x0f; >+ if (!chip->playback_streams && !chip->capture_streams) { >+ /* gcap didn't give any info, switching to old method */ >+ >+ switch (chip->driver_type) { >+ case AZX_DRIVER_ULI: >+ chip->playback_streams = ULI_NUM_PLAYBACK; >+ chip->capture_streams = ULI_NUM_CAPTURE; >+ break; >+ case AZX_DRIVER_ATIHDMI: >+ chip->playback_streams = ATIHDMI_NUM_PLAYBACK; >+ chip->capture_streams = ATIHDMI_NUM_CAPTURE; >+ break; >+ 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 "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; >+ } >+ } >+ /* 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; >+ } >+ /* allocate CORB/RIRB */ >+ if (!chip->single_cmd) { >+ 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); >+ >+ /* 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"); >+ strcpy(card->shortname, driver_short_names[chip->driver_type]); >+ sprintf(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; >+ } >+ >+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); >+ if (!card) { >+ snd_printk(KERN_ERR SFX "Error creating card!\n"); >+ return -ENOMEM; >+ } >+ >+ err = azx_create(card, pci, dev, pci_id->driver_data, &chip); >+ if (err < 0) { >+ snd_card_free(card); >+ return err; >+ } >+ card->private_data = chip; >+ >+ /* create codec instances */ >+ err = azx_codec_create(chip, model[dev], probe_mask[dev]); >+ if (err < 0) { >+ snd_card_free(card); >+ return err; >+ } >+ >+ /* create PCM streams */ >+ err = azx_pcm_create(chip); >+ if (err < 0) { >+ snd_card_free(card); >+ return err; >+ } >+ >+ /* create mixer controls */ >+ err = azx_mixer_create(chip); >+ if (err < 0) { >+ snd_card_free(card); >+ return err; >+ } >+ >+ snd_card_set_dev(card, &pci->dev); >+ >+ err = snd_card_register(card); >+ if (err < 0) { >+ snd_card_free(card); >+ return err; >+ } >+ >+ pci_set_drvdata(pci, card); >+ chip->running = 1; >+ power_down_all_codecs(chip); >+ >+ dev++; >+ 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 struct pci_device_id azx_ids[] = { >+ /* ICH 6..10 */ >+ { PCI_DEVICE(0x8086, 0x2668), .driver_data = AZX_DRIVER_ICH }, >+ { PCI_DEVICE(0x8086, 0x27d8), .driver_data = AZX_DRIVER_ICH }, >+ { PCI_DEVICE(0x8086, 0x269a), .driver_data = AZX_DRIVER_ICH }, >+ { PCI_DEVICE(0x8086, 0x284b), .driver_data = AZX_DRIVER_ICH }, >+ { PCI_DEVICE(0x8086, 0x2911), .driver_data = AZX_DRIVER_ICH }, >+ { PCI_DEVICE(0x8086, 0x293e), .driver_data = AZX_DRIVER_ICH }, >+ { PCI_DEVICE(0x8086, 0x293f), .driver_data = AZX_DRIVER_ICH }, >+ { PCI_DEVICE(0x8086, 0x3a3e), .driver_data = AZX_DRIVER_ICH }, >+ { PCI_DEVICE(0x8086, 0x3a6e), .driver_data = AZX_DRIVER_ICH }, >+ /* SCH */ >+ { PCI_DEVICE(0x8086, 0x811b), .driver_data = AZX_DRIVER_SCH }, >+ /* ATI SB 450/600 */ >+ { PCI_DEVICE(0x1002, 0x437b), .driver_data = AZX_DRIVER_ATI }, >+ { PCI_DEVICE(0x1002, 0x4383), .driver_data = AZX_DRIVER_ATI }, >+ /* ATI HDMI */ >+ { PCI_DEVICE(0x1002, 0x793b), .driver_data = AZX_DRIVER_ATIHDMI }, >+ { PCI_DEVICE(0x1002, 0x7919), .driver_data = AZX_DRIVER_ATIHDMI }, >+ { PCI_DEVICE(0x1002, 0x960f), .driver_data = AZX_DRIVER_ATIHDMI }, >+ { PCI_DEVICE(0x1002, 0xaa00), .driver_data = AZX_DRIVER_ATIHDMI }, >+ { PCI_DEVICE(0x1002, 0xaa08), .driver_data = AZX_DRIVER_ATIHDMI }, >+ { PCI_DEVICE(0x1002, 0xaa10), .driver_data = AZX_DRIVER_ATIHDMI }, >+ { PCI_DEVICE(0x1002, 0xaa18), .driver_data = AZX_DRIVER_ATIHDMI }, >+ { PCI_DEVICE(0x1002, 0xaa20), .driver_data = AZX_DRIVER_ATIHDMI }, >+ { PCI_DEVICE(0x1002, 0xaa28), .driver_data = AZX_DRIVER_ATIHDMI }, >+ { PCI_DEVICE(0x1002, 0xaa30), .driver_data = AZX_DRIVER_ATIHDMI }, >+ { PCI_DEVICE(0x1002, 0xaa38), .driver_data = AZX_DRIVER_ATIHDMI }, >+ { PCI_DEVICE(0x1002, 0xaa40), .driver_data = AZX_DRIVER_ATIHDMI }, >+ { PCI_DEVICE(0x1002, 0xaa48), .driver_data = AZX_DRIVER_ATIHDMI }, >+ /* VIA VT8251/VT8237A */ >+ { PCI_DEVICE(0x1106, 0x3288), .driver_data = AZX_DRIVER_VIA }, >+ /* SIS966 */ >+ { PCI_DEVICE(0x1039, 0x7502), .driver_data = AZX_DRIVER_SIS }, >+ /* ULI M5461 */ >+ { PCI_DEVICE(0x10b9, 0x5461), .driver_data = AZX_DRIVER_ULI }, >+ /* NVIDIA MCP */ >+ { PCI_DEVICE(0x10de, 0x026c), .driver_data = AZX_DRIVER_NVIDIA }, >+ { PCI_DEVICE(0x10de, 0x0371), .driver_data = AZX_DRIVER_NVIDIA }, >+ { PCI_DEVICE(0x10de, 0x03e4), .driver_data = AZX_DRIVER_NVIDIA }, >+ { PCI_DEVICE(0x10de, 0x03f0), .driver_data = AZX_DRIVER_NVIDIA }, >+ { PCI_DEVICE(0x10de, 0x044a), .driver_data = AZX_DRIVER_NVIDIA }, >+ { PCI_DEVICE(0x10de, 0x044b), .driver_data = AZX_DRIVER_NVIDIA }, >+ { PCI_DEVICE(0x10de, 0x055c), .driver_data = AZX_DRIVER_NVIDIA }, >+ { PCI_DEVICE(0x10de, 0x055d), .driver_data = AZX_DRIVER_NVIDIA }, >+ { PCI_DEVICE(0x10de, 0x0774), .driver_data = AZX_DRIVER_NVIDIA }, >+ { PCI_DEVICE(0x10de, 0x0775), .driver_data = AZX_DRIVER_NVIDIA }, >+ { PCI_DEVICE(0x10de, 0x0776), .driver_data = AZX_DRIVER_NVIDIA }, >+ { PCI_DEVICE(0x10de, 0x0777), .driver_data = AZX_DRIVER_NVIDIA }, >+ { PCI_DEVICE(0x10de, 0x07fc), .driver_data = AZX_DRIVER_NVIDIA }, >+ { PCI_DEVICE(0x10de, 0x07fd), .driver_data = AZX_DRIVER_NVIDIA }, >+ { PCI_DEVICE(0x10de, 0x0ac0), .driver_data = AZX_DRIVER_NVIDIA }, >+ { PCI_DEVICE(0x10de, 0x0ac1), .driver_data = AZX_DRIVER_NVIDIA }, >+ { PCI_DEVICE(0x10de, 0x0ac2), .driver_data = AZX_DRIVER_NVIDIA }, >+ { PCI_DEVICE(0x10de, 0x0ac3), .driver_data = AZX_DRIVER_NVIDIA }, >+ { PCI_DEVICE(0x10de, 0x0bd4), .driver_data = AZX_DRIVER_NVIDIA }, >+ { PCI_DEVICE(0x10de, 0x0bd5), .driver_data = AZX_DRIVER_NVIDIA }, >+ { PCI_DEVICE(0x10de, 0x0bd6), .driver_data = AZX_DRIVER_NVIDIA }, >+ { PCI_DEVICE(0x10de, 0x0bd7), .driver_data = AZX_DRIVER_NVIDIA }, >+ /* Teradici */ >+ { PCI_DEVICE(0x6549, 0x1200), .driver_data = AZX_DRIVER_TERA }, >+ { 0, } >+}; >+MODULE_DEVICE_TABLE(pci, azx_ids); >+ >+/* pci_driver definition */ >+static struct pci_driver driver = { >+ .name = "HDA Intel", >+ .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/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h >new file mode 100644 >index 0000000..410e46f >--- /dev/null >+++ b/sound/pci/hda/hda_local.h >@@ -0,0 +1,414 @@ >+/* >+ * 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 >+ >+/* >+ * for mixer controls >+ */ >+#define HDA_COMPOSE_AMP_VAL(nid,chs,idx,dir) \ >+ ((nid) | ((chs)<<16) | ((dir)<<18) | ((idx)<<19)) >+/* mono volume with index (index=0,1,...) (channel=1,2) */ >+#define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ >+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ >+ .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, direction) } >+/* 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) >+/* mono volume */ >+#define HDA_CODEC_VOLUME_MONO(xname, nid, channel, xindex, direction) \ >+ HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, channel, xindex, direction) >+/* stereo volume */ >+#define HDA_CODEC_VOLUME(xname, nid, xindex, direction) \ >+ HDA_CODEC_VOLUME_MONO(xname, nid, 3, xindex, direction) >+/* 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, \ >+ .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) >+ >+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); >+/* 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 SND_HDA_NEEDS_RESUME >+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 **slaves); >+ >+/* 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; >+ 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 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 { >+ const char *label; >+ 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 */ >+ >+struct hda_multi_out { >+ int num_dacs; /* # of DACs, must be more than 1 */ >+ hda_nid_t *dac_nids; /* DAC list */ >+ hda_nid_t hp_nid; /* optional DAC for HP, 0 when not exists */ >+ hda_nid_t extra_out_nid[3]; /* optional DACs, 0 when not exists */ >+ hda_nid_t dig_out_nid; /* digital out audio widget */ >+ 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_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 >+ */ >+int snd_hda_parse_generic_codec(struct hda_codec *codec); >+ >+/* >+ * 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 >+ >+/* >+ * Misc >+ */ >+int snd_hda_check_board_config(struct hda_codec *codec, int num_configs, >+ const char **modelnames, >+ const struct snd_pci_quirk *pci_list); >+int snd_hda_add_new_ctls(struct hda_codec *codec, >+ 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 ping configuration >+ */ >+ >+enum { >+ AUTO_PIN_MIC, >+ AUTO_PIN_FRONT_MIC, >+ AUTO_PIN_LINE, >+ AUTO_PIN_FRONT_LINE, >+ AUTO_PIN_CD, >+ AUTO_PIN_AUX, >+ AUTO_PIN_LAST >+}; >+ >+enum { >+ AUTO_PIN_LINE_OUT, >+ AUTO_PIN_SPEAKER_OUT, >+ AUTO_PIN_HP_OUT >+}; >+ >+extern const char *auto_pin_cfg_labels[AUTO_PIN_LAST]; >+ >+#define AUTO_CFG_MAX_OUTS 5 >+ >+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]; >+ hda_nid_t input_pins[AUTO_PIN_LAST]; >+ hda_nid_t dig_out_pin; >+ hda_nid_t dig_in_pin; >+ hda_nid_t mono_out_pin; >+}; >+ >+#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) >+ >+int snd_hda_parse_pin_def_config(struct hda_codec *codec, >+ struct auto_pin_cfg *cfg, >+ hda_nid_t *ignore_nids); >+ >+/* 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 0x20 >+#define PIN_VREF80 0x24 >+#define PIN_VREF50 0x21 >+#define PIN_OUT 0x40 >+#define PIN_HP 0xc0 >+#define PIN_HP_AMP 0x80 >+ >+/* >+ * 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]; >+} >+ >+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); >+ >+/* >+ * hwdep interface >+ */ >+int snd_hda_create_hwdep(struct hda_codec *codec); >+ >+/* >+ * power-management >+ */ >+ >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+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 { >+ 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); >+#endif /* CONFIG_SND_HDA_POWER_SAVE */ >+ >+#endif /* __SOUND_HDA_LOCAL_H */ >diff --git a/sound/pci/hda/hda_patch.h b/sound/pci/hda/hda_patch.h >new file mode 100644 >index 0000000..2fdf235 >--- /dev/null >+++ b/sound/pci/hda/hda_patch.h >@@ -0,0 +1,20 @@ >+/* >+ * HDA Patches - included by hda_codec.c >+ */ >+ >+/* Realtek codecs */ >+extern struct hda_codec_preset snd_hda_preset_realtek[]; >+/* C-Media codecs */ >+extern struct hda_codec_preset snd_hda_preset_cmedia[]; >+/* Analog Devices codecs */ >+extern struct hda_codec_preset snd_hda_preset_analog[]; >+/* SigmaTel codecs */ >+extern struct hda_codec_preset snd_hda_preset_sigmatel[]; >+/* SiLabs 3054/3055 modem codecs */ >+extern struct hda_codec_preset snd_hda_preset_si3054[]; >+/* ATI HDMI codecs */ >+extern struct hda_codec_preset snd_hda_preset_atihdmi[]; >+/* Conexant audio codec */ >+extern struct hda_codec_preset snd_hda_preset_conexant[]; >+/* VIA codecs */ >+extern struct hda_codec_preset snd_hda_preset_via[]; >diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c >new file mode 100644 >index 0000000..a6210ec >--- /dev/null >+++ b/sound/pci/hda/hda_proc.c >@@ -0,0 +1,663 @@ >+/* >+ * 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 <sound/driver.h> >+#include <linux/init.h> >+#include <sound/core.h> >+#include "hda_codec.h" >+#include "hda_local.h" >+ >+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", >+ }; >+ wid_value &= 0xf; >+ if (names[wid_value]) >+ return names[wid_value]; >+ else >+ return "UNKNOWN Widget"; >+} >+ >+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) >+{ >+ static unsigned int bits[] = { 8, 16, 20, 24, 32 }; >+ int i; >+ >+ pcm = (pcm >> 16) & 0xff; >+ snd_iprintf(buffer, " bits [0x%x]:", pcm); >+ for (i = 0; i < ARRAY_SIZE(bits); i++) >+ if (pcm & (1 << i)) >+ snd_iprintf(buffer, " %d", bits[i]); >+ snd_iprintf(buffer, "\n"); >+} >+ >+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_location(u32 cfg) >+{ >+ static char *bases[7] = { >+ "N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom", >+ }; >+ static unsigned char specials_idx[] = { >+ 0x07, 0x08, >+ 0x17, 0x18, 0x19, >+ 0x37, 0x38 >+ }; >+ static char *specials[] = { >+ "Rear Panel", "Drive Bar", >+ "Riser", "HDMI", "ATAPI", >+ "Mobile-In", "Mobile-Out" >+ }; >+ int i; >+ cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT; >+ if ((cfg & 0x0f) < 7) >+ return bases[cfg & 0x0f]; >+ for (i = 0; i < ARRAY_SIZE(specials_idx); i++) { >+ if (cfg == specials_idx[i]) >+ return specials[i]; >+ } >+ return "UNKNOWN"; >+} >+ >+static const char *get_jack_connection(u32 cfg) >+{ >+ static char *names[16] = { >+ "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" }; >+ static char *jack_types[16] = { >+ "Line Out", "Speaker", "HP Out", "CD", >+ "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand", >+ "Line In", "Aux", "Mic", "Telephony", >+ "SPDIF In", "Digitial In", "Reserved", "Other" >+ }; >+ static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" }; >+ unsigned int caps, val; >+ >+ caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); >+ snd_iprintf(buffer, " Pincap 0x08%x:", 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_LR_SWAP) >+ snd_iprintf(buffer, " R/L"); >+ 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], >+ jack_types[(caps & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT], >+ jack_locations[(caps >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3], >+ 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); >+ unsigned int digi2 = snd_hda_codec_read(codec, nid, 0, >+ AC_VERB_GET_DIGI_CONVERT_2, 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", digi2 & AC_DIG2_CC); >+} >+ >+static const char *get_pwr_state(u32 state) >+{ >+ static const char *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) >+{ >+ int pwr = snd_hda_codec_read(codec, nid, 0, >+ AC_VERB_GET_POWER_STATE, 0); >+ 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) >+ 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_realtek_coef(struct snd_info_buffer *buffer, >+ struct hda_codec *codec, hda_nid_t nid) >+{ >+ int coeff = snd_hda_codec_read(codec, nid, 0, >+ AC_VERB_GET_PROC_COEF, 0); >+ snd_iprintf(buffer, " Processing Coefficient: 0x%02x\n", coeff); >+ coeff = snd_hda_codec_read(codec, nid, 0, >+ AC_VERB_GET_COEF_INDEX, 0); >+ snd_iprintf(buffer, " Coefficient Index: 0x%02x\n", coeff); >+} >+ >+static void print_gpio(struct snd_info_buffer *buffer, >+ struct hda_codec *codec, hda_nid_t nid) >+{ >+ 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; >+ 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\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); >+ /* FIXME: add GPO and GPI pin information */ >+} >+ >+static void print_codec_info(struct snd_info_entry *entry, >+ struct snd_info_buffer *buffer) >+{ >+ struct hda_codec *codec = entry->private_data; >+ char buf[32]; >+ hda_nid_t nid; >+ int i, nodes; >+ >+ snd_hda_get_codec_name(codec, buf, sizeof(buf)); >+ snd_iprintf(buffer, "Codec: %s\n", buf); >+ snd_iprintf(buffer, "Address: %d\n", codec->addr); >+ snd_iprintf(buffer, "Vendor Id: 0x%x\n", codec->vendor_id); >+ snd_iprintf(buffer, "Subsystem Id: 0x%x\n", codec->subsystem_id); >+ 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); >+ >+ 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 = >+ (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; >+ 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) >+ snd_iprintf(buffer, " Stereo"); >+ 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"); >+ snd_iprintf(buffer, "\n"); >+ >+ /* 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_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: "); >+ print_amp_vals(buffer, codec, nid, HDA_INPUT, >+ wid_caps & AC_WCAP_STEREO, >+ wid_type == AC_WID_PIN ? 1 : 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: "); >+ 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); >+ >+ /* NID 0x20 == Realtek Define Registers */ >+ if (codec->vendor_id == 0x10ec && nid == 0x20) >+ print_realtek_coef(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, 32*1024, print_codec_info); >+ return 0; >+} >+ >diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c >new file mode 100644 >index 0000000..809a2a9 >--- /dev/null >+++ b/sound/pci/hda/patch_analog.c >@@ -0,0 +1,4221 @@ >+/* >+ * 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 <sound/driver.h> >+#include <linux/init.h> >+#include <linux/delay.h> >+#include <linux/slab.h> >+#include <linux/pci.h> >+#include <linux/mutex.h> >+ >+#include <sound/core.h> >+#include "hda_codec.h" >+#include "hda_local.h" >+#include "hda_patch.h" >+ >+struct ad198x_spec { >+ struct snd_kcontrol_new *mixers[5]; >+ int num_mixers; >+ >+ 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 need_dac_fix; >+ >+ /* capture */ >+ unsigned int num_adc_nids; >+ hda_nid_t *adc_nids; >+ hda_nid_t dig_in_nid; /* digital-in NID; optional */ >+ >+ /* capture source */ >+ const struct hda_input_mux *input_mux; >+ 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() */ >+ >+ struct mutex amp_mutex; /* PCM volume/mute control mutex */ >+ unsigned int spdif_route; >+ >+ /* dynamic controls, init_verbs and input_mux */ >+ struct auto_pin_cfg autocfg; >+ unsigned int num_kctl_alloc, num_kctl_used; >+ struct snd_kcontrol_new *kctl_alloc; >+ struct hda_input_mux private_imux; >+ hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; >+ >+ unsigned int jack_present :1; >+ >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+ struct hda_loopback_check loopback; >+#endif >+ /* for virtual master */ >+ hda_nid_t vmaster_nid; >+ const char **slave_vols; >+ const char **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 *ad_slave_vols[] = { >+ "Front Playback Volume", >+ "Surround Playback Volume", >+ "Center Playback Volume", >+ "LFE Playback Volume", >+ "Side Playback Volume", >+ "Headphone Playback Volume", >+ "Mono Playback Volume", >+ "Speaker Playback Volume", >+ "IEC958 Playback Volume", >+ NULL >+}; >+ >+static const char *ad_slave_sws[] = { >+ "Front Playback Switch", >+ "Surround Playback Switch", >+ "Center Playback Switch", >+ "LFE Playback Switch", >+ "Side Playback Switch", >+ "Headphone Playback Switch", >+ "Mono Playback Switch", >+ "Speaker Playback Switch", >+ "IEC958 Playback Switch", >+ NULL >+}; >+ >+static int ad198x_build_controls(struct hda_codec *codec) >+{ >+ struct ad198x_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); >+ 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->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_vols)); >+ 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_sws)); >+ 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 >+ >+/* >+ * 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; >+ return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, >+ hinfo); >+} >+ >+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); >+} >+ >+/* >+ * 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); >+} >+ >+/* >+ * 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 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 >+ }, >+}; >+ >+static 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 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 >+ }, >+}; >+ >+static 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; >+ } >+ } >+ >+ return 0; >+} >+ >+static void ad198x_free(struct hda_codec *codec) >+{ >+ struct ad198x_spec *spec = codec->spec; >+ unsigned int i; >+ >+ if (spec->kctl_alloc) { >+ for (i = 0; i < spec->num_kctl_used; i++) >+ kfree(spec->kctl_alloc[i].name); >+ kfree(spec->kctl_alloc); >+ } >+ kfree(codec->spec); >+} >+ >+static 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 >+}; >+ >+ >+/* >+ * EAPD control >+ * the private value = nid | (invert << 8) >+ */ >+#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; >+ 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 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; >+ 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; >+} >+ >+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 hda_nid_t ad1986a_dac_nids[3] = { >+ AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC >+}; >+static hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC }; >+static hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 }; >+ >+static 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 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 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 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", 0x0f, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x18, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x18, 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 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 hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC }; >+ >+/* master controls both pins 0x1a and 0x1b */ >+static 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 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 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", 0x0f, 0x0, HDA_OUTPUT), >+ /* HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x18, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x18, 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 struct hda_input_mux ad1986a_laptop_eapd_capture_source = { >+ .num_items = 3, >+ .items = { >+ { "Mic", 0x0 }, >+ { "Internal Mic", 0x4 }, >+ { "Mix", 0x5 }, >+ }, >+}; >+ >+static struct hda_input_mux ad1986a_automic_capture_source = { >+ .num_items = 2, >+ .items = { >+ { "Mic", 0x0 }, >+ { "Mix", 0x5 }, >+ }, >+}; >+ >+static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = { >+ HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol), >+ HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw), >+ HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Capture Source", >+ .info = ad198x_mux_enum_info, >+ .get = ad198x_mux_enum_get, >+ .put = ad198x_mux_enum_put, >+ }, >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "External Amplifier", >+ .info = ad198x_eapd_info, >+ .get = ad198x_eapd_get, >+ .put = ad198x_eapd_put, >+ .private_value = 0x1b | (1 << 8), /* port-D, inversed */ >+ }, >+ { } /* end */ >+}; >+ >+/* re-connect the mic boost input according to the jack sensing */ >+static void ad1986a_automic(struct hda_codec *codec) >+{ >+ unsigned int present; >+ present = snd_hda_codec_read(codec, 0x1f, 0, AC_VERB_GET_PIN_SENSE, 0); >+ /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */ >+ snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL, >+ (present & AC_PINSENSE_PRESENCE) ? 0 : 2); >+} >+ >+#define AD1986A_MIC_EVENT 0x36 >+ >+static void ad1986a_automic_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ if ((res >> 26) != AD1986A_MIC_EVENT) >+ return; >+ ad1986a_automic(codec); >+} >+ >+static int ad1986a_automic_init(struct hda_codec *codec) >+{ >+ ad198x_init(codec); >+ ad1986a_automic(codec); >+ return 0; >+} >+ >+/* laptop-automute - 2ch only */ >+ >+static void ad1986a_update_hp(struct hda_codec *codec) >+{ >+ 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; >+ unsigned int present; >+ >+ present = snd_hda_codec_read(codec, 0x1a, 0, AC_VERB_GET_PIN_SENSE, 0); >+ /* Lenovo N100 seems to report the reversed bit for HP jack-sensing */ >+ spec->jack_present = !(present & 0x80000000); >+ 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 struct snd_kcontrol_new ad1986a_laptop_automute_mixers[] = { >+ HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Master Playback Switch", >+ .info = snd_hda_mixer_amp_switch_info, >+ .get = snd_hda_mixer_amp_switch_get, >+ .put = ad1986a_hp_master_sw_put, >+ .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT), >+ }, >+ HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Beep Playback Volume", 0x18, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Beep Playback Switch", 0x18, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Capture Source", >+ .info = ad198x_mux_enum_info, >+ .get = ad198x_mux_enum_get, >+ .put = ad198x_mux_enum_put, >+ }, >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "External Amplifier", >+ .info = ad198x_eapd_info, >+ .get = ad198x_eapd_get, >+ .put = ad198x_eapd_put, >+ .private_value = 0x1b | (1 << 8), /* port-D, inversed */ >+ }, >+ { } /* end */ >+}; >+ >+/* >+ * initialization verbs >+ */ >+static 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 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 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 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 struct hda_channel_mode ad1986a_modes[3] = { >+ { 2, ad1986a_ch2_init }, >+ { 4, ad1986a_ch4_init }, >+ { 6, ad1986a_ch6_init }, >+}; >+ >+/* eapd initialization */ >+static struct hda_verb ad1986a_eapd_init_verbs[] = { >+ {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, >+ {} >+}; >+ >+static struct hda_verb ad1986a_automic_verbs[] = { >+ {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ /*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/ >+ {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0}, >+ {0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT}, >+ {} >+}; >+ >+/* Ultra initialization */ >+static struct hda_verb ad1986a_ultra_init[] = { >+ /* eapd initialization */ >+ { 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 struct hda_verb ad1986a_hp_init_verbs[] = { >+ {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT}, >+ {} >+}; >+ >+ >+/* models */ >+enum { >+ AD1986A_6STACK, >+ AD1986A_3STACK, >+ AD1986A_LAPTOP, >+ AD1986A_LAPTOP_EAPD, >+ AD1986A_LAPTOP_AUTOMUTE, >+ AD1986A_ULTRA, >+ AD1986A_MODELS >+}; >+ >+static const char *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", >+}; >+ >+static 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", AD1986A_LAPTOP_EAPD), >+ SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK), >+ SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP), >+ SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_LAPTOP_EAPD), >+ SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_LAPTOP_EAPD), >+ SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_LAPTOP_EAPD), >+ SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA), >+ 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 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_read(codec, nid, 0, >+ AC_VERB_GET_CONFIG_DEFAULT, 0); >+ return get_defcfg_connect(conf) != AC_JACK_PORT_NONE; >+} >+ >+static int patch_ad1986a(struct hda_codec *codec) >+{ >+ struct ad198x_spec *spec; >+ int board_config; >+ >+ spec = kzalloc(sizeof(*spec), GFP_KERNEL); >+ if (spec == NULL) >+ return -ENOMEM; >+ >+ codec->spec = spec; >+ >+ 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; >+ >+ 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->mixers[0] = 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_LAPTOP_AUTOMUTE: >+ spec->mixers[0] = ad1986a_laptop_automute_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; >+ 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; >+ >+ return 0; >+} >+ >+/* >+ * AD1983 specific >+ */ >+ >+#define AD1983_SPDIF_OUT 0x02 >+#define AD1983_DAC 0x03 >+#define AD1983_ADC 0x04 >+ >+static hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC }; >+static hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC }; >+static hda_nid_t ad1983_capsrc_nids[1] = { 0x15 }; >+ >+static 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 char *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 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_MONO("PC Speaker Playback Volume", 0x10, 1, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE_MONO("PC Speaker Playback Switch", 0x10, 1, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 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 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 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; >+ >+ spec = kzalloc(sizeof(*spec), GFP_KERNEL); >+ if (spec == NULL) >+ return -ENOMEM; >+ >+ codec->spec = spec; >+ >+ 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; >+ >+ return 0; >+} >+ >+ >+/* >+ * AD1981 HD specific >+ */ >+ >+#define AD1981_SPDIF_OUT 0x02 >+#define AD1981_DAC 0x03 >+#define AD1981_ADC 0x04 >+ >+static hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC }; >+static hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC }; >+static hda_nid_t ad1981_capsrc_nids[1] = { 0x15 }; >+ >+/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */ >+static 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 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_MONO("PC Speaker Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE_MONO("PC Speaker Playback Switch", 0x0d, 1, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Front Mic Boost", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 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 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, 0xb000}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, >+ /* 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 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 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 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_codec_read(codec, 0x06, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ 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 struct hda_verb mic_jack_on[] = { >+ {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, >+ {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, >+ {} >+ }; >+ static 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_codec_read(codec, 0x08, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ 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 struct hda_input_mux ad1981_hp_capture_source = { >+ .num_items = 3, >+ .items = { >+ { "Mic", 0x0 }, >+ { "Docking-Station", 0x1 }, >+ { "Mix", 0x2 }, >+ }, >+}; >+ >+static struct snd_kcontrol_new ad1981_hp_mixers[] = { >+ HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .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", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Internal Mic Boost", 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 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 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 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", 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 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 *ad1981_models[AD1981_MODELS] = { >+ [AD1981_HP] = "hp", >+ [AD1981_THINKPAD] = "thinkpad", >+ [AD1981_BASIC] = "basic", >+ [AD1981_TOSHIBA] = "toshiba" >+}; >+ >+static struct snd_pci_quirk ad1981_cfg_tbl[] = { >+ SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD), >+ /* All HP models */ >+ SND_PCI_QUIRK(0x103c, 0, "HP nx", AD1981_HP), >+ SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA), >+ /* Lenovo Thinkpad T60/X60/Z6xx */ >+ SND_PCI_QUIRK(0x17aa, 0, "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 board_config; >+ >+ spec = kzalloc(sizeof(*spec), GFP_KERNEL); >+ if (spec == NULL) >+ return -ENOMEM; >+ >+ codec->spec = spec; >+ >+ 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; >+ 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; >+ case AD1981_THINKPAD: >+ spec->mixers[0] = ad1981_thinkpad_mixers; >+ spec->input_mux = &ad1981_thinkpad_capture_source; >+ 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; >+ } >+ 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 hda_nid_t ad1988_6stack_dac_nids[4] = { >+ 0x04, 0x06, 0x05, 0x0a >+}; >+ >+static hda_nid_t ad1988_3stack_dac_nids[3] = { >+ 0x04, 0x05, 0x0a >+}; >+ >+/* for AD1988A revision-2, DAC2-4 are swapped */ >+static hda_nid_t ad1988_6stack_dac_nids_rev2[4] = { >+ 0x04, 0x05, 0x0a, 0x06 >+}; >+ >+static hda_nid_t ad1988_3stack_dac_nids_rev2[3] = { >+ 0x04, 0x0a, 0x06 >+}; >+ >+static hda_nid_t ad1988_adc_nids[3] = { >+ 0x08, 0x09, 0x0f >+}; >+ >+static hda_nid_t ad1988_capsrc_nids[3] = { >+ 0x0c, 0x0d, 0x0e >+}; >+ >+#define AD1988_SPDIF_OUT 0x02 >+#define AD1988_SPDIF_IN 0x07 >+ >+static 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 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; >+} >+ >+/* 6-stack mode */ >+static 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 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 struct snd_kcontrol_new ad1988_6stack_mixers2[] = { >+ 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("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT), >+ >+ 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", 0x39, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT), >+ >+ { } /* end */ >+}; >+ >+/* 3-stack mode */ >+static 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 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 struct snd_kcontrol_new ad1988_3stack_mixers2[] = { >+ 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("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT), >+ >+ 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", 0x39, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 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 struct snd_kcontrol_new ad1988_laptop_mixers[] = { >+ 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("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT), >+ >+ 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", 0x39, 0x0, HDA_OUTPUT), >+ >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "External Amplifier", >+ .info = ad198x_eapd_info, >+ .get = ad198x_eapd_get, >+ .put = ad198x_eapd_put, >+ .private_value = 0x12 | (1 << 8), /* port-D, inversed */ >+ }, >+ >+ { } /* end */ >+}; >+ >+/* capture */ >+static 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 char *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 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", >+ .info = ad1988_spdif_playback_source_info, >+ .get = ad1988_spdif_playback_source_get, >+ .put = ad1988_spdif_playback_source_put, >+ }, >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new ad1988_spdif_in_mixers[] = { >+ HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT), >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new ad1989_spdif_out_mixers[] = { >+ HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT), >+ { } /* end */ >+}; >+ >+/* >+ * initialization verbs >+ */ >+ >+/* >+ * for 6-stack (+dig) >+ */ >+static 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, 0x01}, /* DAC1:04h */ >+ {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}, >+ >+ { } >+}; >+ >+static 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}, >+ /* ADCs; muted */ >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ >+ { } >+}; >+ >+static 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 */ >+ >+ { } >+}; >+ >+/* AD1989 has no ADC -> SPDIF route */ >+static struct hda_verb ad1989_spdif_init_verbs[] = { >+ /* SPDIF out pin */ >+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */ >+ { } >+}; >+ >+/* >+ * verbs for 3stack (+dig) >+ */ >+static 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 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 struct hda_channel_mode ad1988_3stack_modes[2] = { >+ { 2, ad1988_3stack_ch2_init }, >+ { 6, ad1988_3stack_ch6_init }, >+}; >+ >+static 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, 0x01}, /* DAC1:04h */ >+ {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}, >+ /* ADCs; muted */ >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ { } >+}; >+ >+/* >+ * verbs for laptop mode (+dig) >+ */ >+static 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 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 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, 0x01}, /* DAC1:04h */ >+ {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}, >+ /* ADCs; muted */ >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ { } >+}; >+ >+static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res) >+{ >+ if ((res >> 26) != AD1988_HP_EVENT) >+ return; >+ if (snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0) & (1 << 31)) >+ 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 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 >+ */ >+ >+#define NUM_CONTROL_ALLOC 32 >+#define NUM_VERB_ALLOC 32 >+ >+enum { >+ AD_CTL_WIDGET_VOL, >+ AD_CTL_WIDGET_MUTE, >+ AD_CTL_BIND_MUTE, >+}; >+static 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; >+ >+ if (spec->num_kctl_used >= spec->num_kctl_alloc) { >+ int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC; >+ >+ knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */ >+ if (! knew) >+ return -ENOMEM; >+ if (spec->kctl_alloc) { >+ memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc); >+ kfree(spec->kctl_alloc); >+ } >+ spec->kctl_alloc = knew; >+ spec->num_kctl_alloc = num; >+ } >+ >+ knew = &spec->kctl_alloc[spec->num_kctl_used]; >+ *knew = ad1988_control_templates[type]; >+ knew->name = kstrdup(name, GFP_KERNEL); >+ if (! knew->name) >+ return -ENOMEM; >+ knew->private_value = val; >+ spec->num_kctl_used++; >+ return 0; >+} >+ >+#define AD1988_PIN_CD_NID 0x18 >+#define AD1988_PIN_BEEP_NID 0x10 >+ >+static 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 hda_nid_t idx_to_dac[8] = { >+ /* A B C D E F G H */ >+ 0x04, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a >+ }; >+ static hda_nid_t idx_to_dac_rev2[8] = { >+ /* A B C D E F G H */ >+ 0x04, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06 >+ }; >+ if (is_rev2(codec)) >+ return idx_to_dac_rev2[idx]; >+ else >+ return idx_to_dac[idx]; >+} >+ >+static 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 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 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 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->multiout.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 *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 idx, err; >+ char name[32]; >+ >+ if (! pin) >+ return 0; >+ >+ idx = ad1988_pin_idx(pin); >+ nid = ad1988_idx_to_dac(codec, idx); >+ /* 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); >+ if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name, >+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 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 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", 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 ad198x_spec *spec, >+ const struct auto_pin_cfg *cfg) >+{ >+ struct hda_input_mux *imux = &spec->private_imux; >+ int i, err; >+ >+ for (i = 0; i < AUTO_PIN_LAST; i++) { >+ err = new_analog_input(spec, cfg->input_pins[i], >+ auto_pin_cfg_labels[i], >+ i <= AUTO_PIN_FRONT_MIC); >+ if (err < 0) >+ return err; >+ imux->items[imux->num_items].label = auto_pin_cfg_labels[i]; >+ imux->items[imux->num_items].index = ad1988_pin_to_adc_idx(cfg->input_pins[i]); >+ imux->num_items++; >+ } >+ imux->items[imux->num_items].label = "Mix"; >+ imux->items[imux->num_items].index = 9; >+ imux->num_items++; >+ >+ 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 04 */ >+ snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x01); >+ 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; >+ int i, idx; >+ >+ for (i = 0; i < AUTO_PIN_LAST; i++) { >+ hda_nid_t nid = spec->autocfg.input_pins[i]; >+ if (! nid) >+ continue; >+ 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, >+ i <= AUTO_PIN_FRONT_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(spec, &spec->autocfg)) < 0) >+ return err; >+ >+ spec->multiout.max_channels = spec->multiout.num_dacs * 2; >+ >+ if (spec->autocfg.dig_out_pin) >+ spec->multiout.dig_out_nid = AD1988_SPDIF_OUT; >+ if (spec->autocfg.dig_in_pin) >+ spec->dig_in_nid = AD1988_SPDIF_IN; >+ >+ if (spec->kctl_alloc) >+ spec->mixers[spec->num_mixers++] = spec->kctl_alloc; >+ >+ 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 *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 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), >+ {} >+}; >+ >+static int patch_ad1988(struct hda_codec *codec) >+{ >+ struct ad198x_spec *spec; >+ int 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: Unknown model for AD1988, trying auto-probe from BIOS...\n"); >+ board_config = AD1988_AUTO; >+ } >+ >+ if (board_config == AD1988_AUTO) { >+ /* automatic parse from the BIOS config */ >+ int 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; >+ } >+ } >+ >+ 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->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; >+ } >+ >+ 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; >+ } 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; >+ >+ 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; >+ >+ 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 hda_nid_t ad1884_dac_nids[1] = { >+ 0x04, >+}; >+ >+static hda_nid_t ad1884_adc_nids[2] = { >+ 0x08, 0x09, >+}; >+ >+static hda_nid_t ad1884_capsrc_nids[2] = { >+ 0x0c, 0x0d, >+}; >+ >+#define AD1884_SPDIF_OUT 0x02 >+ >+static struct hda_input_mux ad1884_capture_source = { >+ .num_items = 4, >+ .items = { >+ { "Front Mic", 0x0 }, >+ { "Mic", 0x1 }, >+ { "CD", 0x2 }, >+ { "Mix", 0x3 }, >+ }, >+}; >+ >+static 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("PC Speaker Playback Volume", 0x20, 0x03, HDA_INPUT), >+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x20, 0x03, HDA_INPUT), >+ HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Digital Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT), >+ */ >+ HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Boost", 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 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 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_OUT_MUTE}, >+ /* Port-C (rear mic) pin */ >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x15, 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)}, >+ /* 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 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 *ad1884_slave_vols[] = { >+ "PCM Playback Volume", >+ "Mic Playback Volume", >+ "Mono Playback Volume", >+ "Front Mic Playback Volume", >+ "Mic Playback Volume", >+ "CD Playback Volume", >+ "Internal Mic Playback Volume", >+ "Docking Mic Playback Volume" >+ "Beep Playback Volume", >+ "IEC958 Playback Volume", >+ NULL >+}; >+ >+static int patch_ad1884(struct hda_codec *codec) >+{ >+ struct ad198x_spec *spec; >+ >+ spec = kzalloc(sizeof(*spec), GFP_KERNEL); >+ if (spec == NULL) >+ return -ENOMEM; >+ >+ mutex_init(&spec->amp_mutex); >+ codec->spec = spec; >+ >+ spec->multiout.max_channels = 2; >+ spec->multiout.num_dacs = ARRAY_SIZE(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; >+ >+ codec->patch_ops = ad198x_patch_ops; >+ >+ return 0; >+} >+ >+/* >+ * Lenovo Thinkpad T61/X61 >+ */ >+static 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 struct hda_input_mux ad1984_dell_desktop_capture_source = { >+ .num_items = 3, >+ .items = { >+ { "Front Mic", 0x0 }, >+ { "Line-In", 0x1 }, >+ { "Mix", 0x3 }, >+ }, >+}; >+ >+ >+static 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("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT), >+ HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, 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 */ >+}; >+ >+/* additional verbs */ >+static 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_MUTE}, >+ /* 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 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("PC Speaker Playback Volume", 0x20, 0x03, HDA_INPUT), >+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x20, 0x03, HDA_INPUT), >+ */ >+ HDA_CODEC_VOLUME("Line-In Boost", 0x15, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Boost", 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 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 *ad1984_models[AD1984_MODELS] = { >+ [AD1984_BASIC] = "basic", >+ [AD1984_THINKPAD] = "thinkpad", >+ [AD1984_DELL_DESKTOP] = "dell_desktop", >+}; >+ >+static struct snd_pci_quirk ad1984_cfg_tbl[] = { >+ /* Lenovo Thinkpad T61/X61 */ >+ SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1984_THINKPAD), >+ SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", 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: >+ 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; >+ 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 hda_nid_t ad1884a_dac_nids[1] = { >+ 0x03, >+}; >+ >+#define ad1884a_adc_nids ad1884_adc_nids >+#define ad1884a_capsrc_nids ad1884_capsrc_nids >+ >+#define AD1884A_SPDIF_OUT 0x02 >+ >+static struct hda_input_mux ad1884a_capture_source = { >+ .num_items = 5, >+ .items = { >+ { "Front Mic", 0x0 }, >+ { "Mic", 0x4 }, >+ { "Line", 0x1 }, >+ { "CD", 0x2 }, >+ { "Mix", 0x3 }, >+ }, >+}; >+ >+static struct snd_kcontrol_new ad1884a_base_mixers[] = { >+ HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), >+ HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT), >+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT), >+ HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Boost", 0x15, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ /* The multiple "Capture Source" controls confuse alsamixer >+ * So call somewhat different.. >+ */ >+ /* .name = "Capture Source", */ >+ .name = "Input Source", >+ .count = 2, >+ .info = ad198x_mux_enum_info, >+ .get = ad198x_mux_enum_get, >+ .put = ad198x_mux_enum_put, >+ }, >+ /* SPDIF controls */ >+ HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", >+ /* identical with ad1983 */ >+ .info = ad1983_spdif_route_info, >+ .get = ad1983_spdif_route_get, >+ .put = ad1983_spdif_route_put, >+ }, >+ { } /* end */ >+}; >+ >+/* >+ * initialization verbs >+ */ >+static struct hda_verb ad1884a_init_verbs[] = { >+ /* DACs; unmute as default */ >+ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ >+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ >+ /* Port-A (HP) mixer - route only from analog mixer */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ /* Port-A pin */ >+ {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* Port-D (Line-out) mixer - route only from analog mixer */ >+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ /* Port-D pin */ >+ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* Mono-out mixer - route only from analog mixer */ >+ {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ /* Mono-out pin */ >+ {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* Port-B (front mic) pin */ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* Port-C (rear line-in) pin */ >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* Port-E (rear mic) pin */ >+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */ >+ /* Port-F (CD) pin */ >+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* Analog mixer; mute as default */ >+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, >+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, >+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */ >+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, >+ /* Analog Mix output amp */ >+ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* capture sources */ >+ {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* SPDIF output amp */ >+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */ >+ { } /* end */ >+}; >+ >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+static struct hda_amp_list ad1884a_loopbacks[] = { >+ { 0x20, HDA_INPUT, 0 }, /* Front Mic */ >+ { 0x20, HDA_INPUT, 1 }, /* Mic */ >+ { 0x20, HDA_INPUT, 2 }, /* CD */ >+ { 0x20, HDA_INPUT, 4 }, /* Docking */ >+ { } /* end */ >+}; >+#endif >+ >+/* >+ * Laptop model >+ * >+ * Port A: Headphone jack >+ * Port B: MIC jack >+ * Port C: Internal MIC >+ * Port D: Dock Line Out (if enabled) >+ * Port E: Dock Line In (if enabled) >+ * Port F: Internal speakers >+ */ >+ >+static struct hda_input_mux ad1884a_laptop_capture_source = { >+ .num_items = 4, >+ .items = { >+ { "Mic", 0x0 }, /* port-B */ >+ { "Internal Mic", 0x1 }, /* port-C */ >+ { "Dock Mic", 0x4 }, /* port-E */ >+ { "Mix", 0x3 }, >+ }, >+}; >+ >+static struct snd_kcontrol_new ad1884a_laptop_mixers[] = { >+ HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), >+ HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT), >+ HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT), >+ HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT), >+ HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT), >+ HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ /* The multiple "Capture Source" controls confuse alsamixer >+ * So call somewhat different.. >+ */ >+ /* .name = "Capture Source", */ >+ .name = "Input Source", >+ .count = 2, >+ .info = ad198x_mux_enum_info, >+ .get = ad198x_mux_enum_get, >+ .put = ad198x_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new ad1884a_mobile_mixers[] = { >+ HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), >+ HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), >+ HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT), >+ HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, 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_codec_read(codec, 0x11, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); >+ snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE, >+ present ? 0x00 : 0x02); >+} >+ >+/* switch to external mic if plugged */ >+static void ad1884a_hp_automic(struct hda_codec *codec) >+{ >+ unsigned int present; >+ >+ present = snd_hda_codec_read(codec, 0x14, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ 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; >+} >+ >+/* additional verbs for laptop model */ >+static struct hda_verb ad1884a_laptop_verbs[] = { >+ /* Port-A (HP) pin - always unmuted */ >+ {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ /* Port-F (int speaker) mixer - route only from analog mixer */ >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ /* Port-F pin */ >+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* 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 */ >+ /* 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}, >+ {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT}, >+ { } /* end */ >+}; >+ >+/* >+ * Thinkpad X300 >+ * 0x11 - HP >+ * 0x12 - speaker >+ * 0x14 - mic-in >+ * 0x17 - built-in mic >+ */ >+ >+static struct hda_verb ad1984a_thinkpad_verbs[] = { >+ /* HP unmute */ >+ {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ /* analog mix */ >+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, >+ /* turn on EAPD */ >+ {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, >+ /* unsolicited event for pin-sense */ >+ {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, >+ /* internal mic - dmic */ >+ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ /* set magic COEFs for dmic */ >+ {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7}, >+ {0x01, AC_VERB_SET_PROC_COEF, 0x08}, >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = { >+ HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), >+ HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT), >+ HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT), >+ HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Capture Source", >+ .info = ad198x_mux_enum_info, >+ .get = ad198x_mux_enum_get, >+ .put = ad198x_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+static struct hda_input_mux ad1984a_thinkpad_capture_source = { >+ .num_items = 3, >+ .items = { >+ { "Mic", 0x0 }, >+ { "Internal Mic", 0x5 }, >+ { "Mix", 0x3 }, >+ }, >+}; >+ >+/* mute internal speaker if HP is plugged */ >+static void ad1984a_thinkpad_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ >+ present = snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0) >+ & AC_PINSENSE_PRESENCE; >+ snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); >+} >+ >+/* unsolicited event for HP jack sensing */ >+static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ if ((res >> 26) != AD1884A_HP_EVENT) >+ return; >+ ad1984a_thinkpad_automute(codec); >+} >+ >+/* initialize jack-sensing, too */ >+static int ad1984a_thinkpad_init(struct hda_codec *codec) >+{ >+ ad198x_init(codec); >+ ad1984a_thinkpad_automute(codec); >+ return 0; >+} >+ >+/* >+ */ >+ >+enum { >+ AD1884A_DESKTOP, >+ AD1884A_LAPTOP, >+ AD1884A_MOBILE, >+ AD1884A_THINKPAD, >+ AD1884A_MODELS >+}; >+ >+static const char *ad1884a_models[AD1884A_MODELS] = { >+ [AD1884A_DESKTOP] = "desktop", >+ [AD1884A_LAPTOP] = "laptop", >+ [AD1884A_MOBILE] = "mobile", >+ [AD1884A_THINKPAD] = "thinkpad", >+}; >+ >+static struct snd_pci_quirk ad1884a_cfg_tbl[] = { >+ SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE), >+ SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD), >+ {} >+}; >+ >+static int patch_ad1884a(struct hda_codec *codec) >+{ >+ struct ad198x_spec *spec; >+ int board_config; >+ >+ spec = kzalloc(sizeof(*spec), GFP_KERNEL); >+ if (spec == NULL) >+ return -ENOMEM; >+ >+ mutex_init(&spec->amp_mutex); >+ codec->spec = spec; >+ >+ spec->multiout.max_channels = 2; >+ spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids); >+ spec->multiout.dac_nids = ad1884a_dac_nids; >+ spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT; >+ spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids); >+ spec->adc_nids = ad1884a_adc_nids; >+ spec->capsrc_nids = ad1884a_capsrc_nids; >+ spec->input_mux = &ad1884a_capture_source; >+ spec->num_mixers = 1; >+ spec->mixers[0] = ad1884a_base_mixers; >+ spec->num_init_verbs = 1; >+ spec->init_verbs[0] = ad1884a_init_verbs; >+ spec->spdif_route = 0; >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+ spec->loopback.amplist = ad1884a_loopbacks; >+#endif >+ codec->patch_ops = ad198x_patch_ops; >+ >+ /* override some parameters */ >+ board_config = snd_hda_check_board_config(codec, AD1884A_MODELS, >+ ad1884a_models, >+ ad1884a_cfg_tbl); >+ switch (board_config) { >+ case AD1884A_LAPTOP: >+ spec->mixers[0] = ad1884a_laptop_mixers; >+ spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs; >+ spec->multiout.dig_out_nid = 0; >+ spec->input_mux = &ad1884a_laptop_capture_source; >+ codec->patch_ops.unsol_event = ad1884a_hp_unsol_event; >+ codec->patch_ops.init = ad1884a_hp_init; >+ break; >+ case AD1884A_MOBILE: >+ spec->mixers[0] = ad1884a_mobile_mixers; >+ spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs; >+ spec->multiout.dig_out_nid = 0; >+ codec->patch_ops.unsol_event = ad1884a_hp_unsol_event; >+ codec->patch_ops.init = ad1884a_hp_init; >+ break; >+ case AD1884A_THINKPAD: >+ spec->mixers[0] = ad1984a_thinkpad_mixers; >+ spec->init_verbs[spec->num_init_verbs++] = >+ ad1984a_thinkpad_verbs; >+ spec->multiout.dig_out_nid = 0; >+ spec->input_mux = &ad1984a_thinkpad_capture_source; >+ codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event; >+ codec->patch_ops.init = ad1984a_thinkpad_init; >+ break; >+ } >+ >+ return 0; >+} >+ >+ >+/* >+ * AD1882 >+ * >+ * port-A - front hp-out >+ * 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 hda_nid_t ad1882_dac_nids[3] = { >+ 0x04, 0x03, 0x05 >+}; >+ >+static hda_nid_t ad1882_adc_nids[2] = { >+ 0x08, 0x09, >+}; >+ >+static hda_nid_t ad1882_capsrc_nids[2] = { >+ 0x0c, 0x0d, >+}; >+ >+#define AD1882_SPDIF_OUT 0x02 >+ >+/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */ >+static struct hda_input_mux ad1882_capture_source = { >+ .num_items = 5, >+ .items = { >+ { "Front Mic", 0x1 }, >+ { "Mic", 0x4 }, >+ { "Line", 0x2 }, >+ { "CD", 0x3 }, >+ { "Mix", 0x7 }, >+ }, >+}; >+ >+static 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("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), >+ HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x07, HDA_INPUT), >+ HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x07, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Line-In Boost", 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 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 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 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 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 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 struct hda_channel_mode ad1882_modes[3] = { >+ { 2, ad1882_ch2_init }, >+ { 4, ad1882_ch4_init }, >+ { 6, ad1882_ch6_init }, >+}; >+ >+/* >+ * initialization verbs >+ */ >+static 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 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 *ad1882_models[AD1986A_MODELS] = { >+ [AD1882_3STACK] = "3stack", >+ [AD1882_6STACK] = "6stack", >+}; >+ >+ >+static int patch_ad1882(struct hda_codec *codec) >+{ >+ struct ad198x_spec *spec; >+ int board_config; >+ >+ spec = kzalloc(sizeof(*spec), GFP_KERNEL); >+ if (spec == NULL) >+ return -ENOMEM; >+ >+ mutex_init(&spec->amp_mutex); >+ codec->spec = spec; >+ >+ spec->multiout.max_channels = 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; >+ spec->input_mux = &ad1882_capture_source; >+ spec->num_mixers = 1; >+ spec->mixers[0] = ad1882_base_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 = 2; >+ spec->mixers[1] = 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 = 2; >+ spec->mixers[1] = ad1882_6stack_mixers; >+ break; >+ } >+ return 0; >+} >+ >+ >+/* >+ * patch entries >+ */ >+struct hda_codec_preset snd_hda_preset_analog[] = { >+ { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a }, >+ { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 }, >+ { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a }, >+ { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 }, >+ { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a }, >+ { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a }, >+ { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 }, >+ { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 }, >+ { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 }, >+ { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a }, >+ { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 }, >+ { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 }, >+ { .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 }, >+ { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 }, >+ {} /* terminator */ >+}; >diff --git a/sound/pci/hda/patch_atihdmi.c b/sound/pci/hda/patch_atihdmi.c >new file mode 100644 >index 0000000..108b0f1 >--- /dev/null >+++ b/sound/pci/hda/patch_atihdmi.c >@@ -0,0 +1,171 @@ >+/* >+ * Universal Interface for Intel High Definition Audio Codec >+ * >+ * HD audio interface patch for ATI HDMI codecs >+ * >+ * Copyright (c) 2006 ATI Technologies 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 <sound/driver.h> >+#include <linux/init.h> >+#include <linux/delay.h> >+#include <linux/slab.h> >+#include <sound/core.h> >+#include "hda_codec.h" >+#include "hda_local.h" >+#include "hda_patch.h" >+ >+struct atihdmi_spec { >+ struct hda_multi_out multiout; >+ >+ struct hda_pcm pcm_rec; >+}; >+ >+static struct hda_verb atihdmi_basic_init[] = { >+ /* enable digital output on pin widget */ >+ { 0x03, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ {} /* terminator */ >+}; >+ >+/* >+ * Controls >+ */ >+static int atihdmi_build_controls(struct hda_codec *codec) >+{ >+ struct atihdmi_spec *spec = codec->spec; >+ int err; >+ >+ err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); >+ if (err < 0) >+ return err; >+ >+ return 0; >+} >+ >+static int atihdmi_init(struct hda_codec *codec) >+{ >+ snd_hda_sequence_write(codec, atihdmi_basic_init); >+ /* SI codec requires to unmute the pin */ >+ if (get_wcaps(codec, 0x03) & AC_WCAP_OUT_AMP) >+ snd_hda_codec_write(codec, 0x03, 0, AC_VERB_SET_AMP_GAIN_MUTE, >+ AMP_OUT_UNMUTE); >+ return 0; >+} >+ >+/* >+ * Digital out >+ */ >+static int atihdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, >+ struct hda_codec *codec, >+ struct snd_pcm_substream *substream) >+{ >+ struct atihdmi_spec *spec = codec->spec; >+ return snd_hda_multi_out_dig_open(codec, &spec->multiout); >+} >+ >+static int atihdmi_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, >+ struct hda_codec *codec, >+ struct snd_pcm_substream *substream) >+{ >+ struct atihdmi_spec *spec = codec->spec; >+ return snd_hda_multi_out_dig_close(codec, &spec->multiout); >+} >+ >+static int atihdmi_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 atihdmi_spec *spec = codec->spec; >+ return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, >+ format, substream); >+} >+ >+static struct hda_pcm_stream atihdmi_pcm_digital_playback = { >+ .substreams = 1, >+ .channels_min = 2, >+ .channels_max = 2, >+ .nid = 0x2, /* NID to query formats and rates and setup streams */ >+ .ops = { >+ .open = atihdmi_dig_playback_pcm_open, >+ .close = atihdmi_dig_playback_pcm_close, >+ .prepare = atihdmi_dig_playback_pcm_prepare >+ }, >+}; >+ >+static int atihdmi_build_pcms(struct hda_codec *codec) >+{ >+ struct atihdmi_spec *spec = codec->spec; >+ struct hda_pcm *info = &spec->pcm_rec; >+ >+ codec->num_pcms = 1; >+ codec->pcm_info = info; >+ >+ info->name = "ATI HDMI"; >+ info->pcm_type = HDA_PCM_TYPE_HDMI; >+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = atihdmi_pcm_digital_playback; >+ >+ return 0; >+} >+ >+static void atihdmi_free(struct hda_codec *codec) >+{ >+ kfree(codec->spec); >+} >+ >+static struct hda_codec_ops atihdmi_patch_ops = { >+ .build_controls = atihdmi_build_controls, >+ .build_pcms = atihdmi_build_pcms, >+ .init = atihdmi_init, >+ .free = atihdmi_free, >+}; >+ >+static int patch_atihdmi(struct hda_codec *codec) >+{ >+ struct atihdmi_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 = 0x2; /* NID for copying analog to digital, >+ * seems to be unused in pure-digital >+ * case. */ >+ >+ codec->patch_ops = atihdmi_patch_ops; >+ >+ return 0; >+} >+ >+/* >+ * patch entries >+ */ >+struct hda_codec_preset snd_hda_preset_atihdmi[] = { >+ { .id = 0x1002793c, .name = "ATI RS600 HDMI", .patch = patch_atihdmi }, >+ { .id = 0x10027919, .name = "ATI RS600 HDMI", .patch = patch_atihdmi }, >+ { .id = 0x1002791a, .name = "ATI RS690/780 HDMI", .patch = patch_atihdmi }, >+ { .id = 0x1002aa01, .name = "ATI R6xx HDMI", .patch = patch_atihdmi }, >+ { .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_atihdmi }, >+ { .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_atihdmi }, >+ {} /* terminator */ >+}; >diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c >new file mode 100644 >index 0000000..e6e16c6 >--- /dev/null >+++ b/sound/pci/hda/patch_cmedia.c >@@ -0,0 +1,744 @@ >+/* >+ * 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 <sound/driver.h> >+#include <linux/init.h> >+#include <linux/delay.h> >+#include <linux/slab.h> >+#include <linux/pci.h> >+#include <sound/core.h> >+#include "hda_codec.h" >+#include "hda_local.h" >+#include "hda_patch.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 */ >+ 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 deafault 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 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 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 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 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 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("PC Speaker Playback Volume", 0x23, 0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x23, 0, HDA_OUTPUT), >+ { } /* end */ >+}; >+ >+/* >+ * shared I/O pins >+ */ >+static 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 struct hda_input_mux cmi9880_basic_mux = { >+ .num_items = 4, >+ .items = { >+ { "Front Mic", 0x5 }, >+ { "Rear Mic", 0x2 }, >+ { "Line", 0x1 }, >+ { "CD", 0x7 }, >+ } >+}; >+ >+static 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 hda_nid_t cmi9880_dac_nids[4] = { >+ 0x03, 0x04, 0x05, 0x06 >+}; >+/* ADC0, ADC1 */ >+static hda_nid_t cmi9880_adc_nids[2] = { >+ 0x08, 0x09 >+}; >+ >+#define CMI_DIG_OUT_NID 0x07 >+#define CMI_DIG_IN_NID 0x0a >+ >+/* >+ */ >+static 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 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; >+ int 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); >+ 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; >+ } >+ 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, len; >+ >+ /* 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++) { >+ hda_nid_t conn[4]; >+ 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 */ >+ len = snd_hda_get_connections(codec, nid, conn, 4); >+ for (k = 0; k < len; k++) >+ if (conn[k] == spec->dac_nids[i]) { >+ spec->multi_init[j].param = k; >+ break; >+ } >+ 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 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 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 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 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 *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 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 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: Unknown model for CMI9880\n"); >+ 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_read(codec, 0x0f, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); >+ port_f = snd_hda_codec_read(codec, 0x10, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); >+ 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_read(codec, 0x1f, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); >+ port_h = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); >+ 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_read(codec, 0x13, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); >+ port_spdifo = snd_hda_codec_read(codec, 0x12, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); >+ 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 >+ */ >+struct hda_codec_preset snd_hda_preset_cmedia[] = { >+ { .id = 0x13f69880, .name = "CMI9880", .patch = patch_cmi9880 }, >+ { .id = 0x434d4980, .name = "CMI9880", .patch = patch_cmi9880 }, >+ {} /* terminator */ >+}; >diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c >new file mode 100644 >index 0000000..c15088b >--- /dev/null >+++ b/sound/pci/hda/patch_conexant.c >@@ -0,0 +1,1791 @@ >+/* >+ * 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 <sound/driver.h> >+#include <linux/init.h> >+#include <linux/delay.h> >+#include <linux/slab.h> >+#include <linux/pci.h> >+#include <sound/core.h> >+#include "hda_codec.h" >+#include "hda_local.h" >+#include "hda_patch.h" >+ >+#define CXT_PIN_DIR_IN 0x00 >+#define CXT_PIN_DIR_OUT 0x01 >+#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 >+ >+ >+ >+struct conexant_spec { >+ >+ struct snd_kcontrol_new *mixers[5]; >+ int num_mixers; >+ >+ 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 need_dac_fix; >+ >+ /* capture */ >+ unsigned int num_adc_nids; >+ 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; >+ >+ /* capture source */ >+ const struct hda_input_mux *input_mux; >+ 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() */ >+ >+ struct mutex amp_mutex; /* PCM volume/mute control mutex */ >+ unsigned int spdif_route; >+ >+ /* dynamic controls, init_verbs and input_mux */ >+ struct auto_pin_cfg autocfg; >+ unsigned int num_kctl_alloc, num_kctl_used; >+ struct snd_kcontrol_new *kctl_alloc; >+ struct hda_input_mux private_imux; >+ hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; >+ >+}; >+ >+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; >+ 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]); >+ return 0; >+} >+ >+ >+ >+static 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 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 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 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 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 (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; >+ } >+ } >+ >+ 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 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) >+{ >+ struct conexant_spec *spec = codec->spec; >+ unsigned int i; >+ >+ if (spec->kctl_alloc) { >+ for (i = 0; i < spec->num_kctl_used; i++) >+ kfree(spec->kctl_alloc[i].name); >+ kfree(spec->kctl_alloc); >+ } >+ >+ kfree(codec->spec); >+} >+ >+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); >+ 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; >+ } >+ return 0; >+} >+ >+static struct hda_codec_ops conexant_patch_ops = { >+ .build_controls = conexant_build_controls, >+ .build_pcms = conexant_build_pcms, >+ .init = conexant_init, >+ .free = conexant_free, >+}; >+ >+/* >+ * 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 hda_nid_t cxt5045_dac_nids[1] = { 0x19 }; >+static hda_nid_t cxt5045_adc_nids[1] = { 0x1a }; >+static hda_nid_t cxt5045_capsrc_nids[1] = { 0x1a }; >+#define CXT5045_SPDIF_OUT 0x18 >+ >+static struct hda_channel_mode cxt5045_modes[1] = { >+ { 2, NULL }, >+}; >+ >+static struct hda_input_mux cxt5045_capture_source = { >+ .num_items = 2, >+ .items = { >+ { "IntMic", 0x1 }, >+ { "ExtMic", 0x2 }, >+ } >+}; >+ >+static struct hda_input_mux cxt5045_capture_source_benq = { >+ .num_items = 3, >+ .items = { >+ { "IntMic", 0x1 }, >+ { "ExtMic", 0x2 }, >+ { "LineIn", 0x3 }, >+ } >+}; >+ >+static struct hda_input_mux cxt5045_capture_source_hp530 = { >+ .num_items = 2, >+ .items = { >+ { "ExtMic", 0x1 }, >+ { "IntMic", 0x2 }, >+ } >+}; >+ >+/* turn on/off EAPD (+ mute HP) as a master switch */ >+static int cxt5045_hp_master_sw_put(struct snd_kcontrol *kcontrol, >+ struct snd_ctl_elem_value *ucontrol) >+{ >+ 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 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 struct hda_verb mic_jack_on[] = { >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, >+ {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, >+ {} >+ }; >+ static 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_codec_read(codec, 0x12, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ 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_codec_read(codec, 0x11, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ >+ 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 struct snd_kcontrol_new cxt5045_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 >+ }, >+ HDA_CODEC_VOLUME("Int Mic Capture Volume", 0x1a, 0x01, HDA_INPUT), >+ HDA_CODEC_MUTE("Int Mic Capture Switch", 0x1a, 0x01, HDA_INPUT), >+ HDA_CODEC_VOLUME("Ext Mic Capture Volume", 0x1a, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Ext Mic Capture Switch", 0x1a, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("PCM Playback Volume", 0x17, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("PCM Playback Switch", 0x17, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x17, 0x1, HDA_INPUT), >+ HDA_CODEC_MUTE("Int Mic Playback Switch", 0x17, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("Ext Mic Playback Volume", 0x17, 0x2, HDA_INPUT), >+ HDA_CODEC_MUTE("Ext 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 struct snd_kcontrol_new cxt5045_benq_mixers[] = { >+ HDA_CODEC_VOLUME("Line In Capture Volume", 0x1a, 0x03, HDA_INPUT), >+ HDA_CODEC_MUTE("Line In Capture Switch", 0x1a, 0x03, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line In Playback Volume", 0x17, 0x3, HDA_INPUT), >+ HDA_CODEC_MUTE("Line In Playback Switch", 0x17, 0x3, HDA_INPUT), >+ >+ {} >+}; >+ >+static struct snd_kcontrol_new cxt5045_mixers_hp530[] = { >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Capture Source", >+ .info = conexant_mux_enum_info, >+ .get = conexant_mux_enum_get, >+ .put = conexant_mux_enum_put >+ }, >+ HDA_CODEC_VOLUME("Int Mic Capture Volume", 0x1a, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Int Mic Capture Switch", 0x1a, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Ext Mic Capture Volume", 0x1a, 0x01, HDA_INPUT), >+ HDA_CODEC_MUTE("Ext Mic Capture Switch", 0x1a, 0x01, HDA_INPUT), >+ HDA_CODEC_VOLUME("PCM Playback Volume", 0x17, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("PCM Playback Switch", 0x17, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x17, 0x2, HDA_INPUT), >+ HDA_CODEC_MUTE("Int Mic Playback Switch", 0x17, 0x2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Ext Mic Playback Volume", 0x17, 0x1, HDA_INPUT), >+ HDA_CODEC_MUTE("Ext Mic Playback Switch", 0x17, 0x1, HDA_INPUT), >+ HDA_BIND_VOL("Master Playback Volume", &cxt5045_hp_bind_master_vol), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Master Playback Switch", >+ .info = cxt_eapd_info, >+ .get = cxt_eapd_get, >+ .put = cxt5045_hp_master_sw_put, >+ .private_value = 0x10, >+ }, >+ >+ {} >+}; >+ >+static struct hda_verb cxt5045_init_verbs[] = { >+ /* Line in, Mic */ >+ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, >+ {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: Int 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 struct hda_verb cxt5045_benq_init_verbs[] = { >+ /* Int 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: Int 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 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 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 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 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("Node 11 Playback Volume", 0x11, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Node 11 Playback Switch", 0x11, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Node 12 Playback Volume", 0x12, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Node 12 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("Mixer-1 Volume", 0x17, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mixer-1 Switch", 0x17, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mixer-2 Volume", 0x17, 0x1, HDA_INPUT), >+ HDA_CODEC_MUTE("Mixer-2 Switch", 0x17, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mixer-3 Volume", 0x17, 0x2, HDA_INPUT), >+ HDA_CODEC_MUTE("Mixer-3 Switch", 0x17, 0x2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mixer-4 Volume", 0x17, 0x3, HDA_INPUT), >+ HDA_CODEC_MUTE("Mixer-4 Switch", 0x17, 0x3, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mixer-5 Volume", 0x17, 0x4, HDA_INPUT), >+ HDA_CODEC_MUTE("Mixer-5 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("Input-1 Volume", 0x1a, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Input-1 Switch", 0x1a, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Input-2 Volume", 0x1a, 0x1, HDA_INPUT), >+ HDA_CODEC_MUTE("Input-2 Switch", 0x1a, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("Input-3 Volume", 0x1a, 0x2, HDA_INPUT), >+ HDA_CODEC_MUTE("Input-3 Switch", 0x1a, 0x2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Input-4 Volume", 0x1a, 0x3, HDA_INPUT), >+ HDA_CODEC_MUTE("Input-4 Switch", 0x1a, 0x3, HDA_INPUT), >+ HDA_CODEC_VOLUME("Input-5 Volume", 0x1a, 0x4, HDA_INPUT), >+ HDA_CODEC_MUTE("Input-5 Switch", 0x1a, 0x4, HDA_INPUT), >+ { } /* end */ >+}; >+ >+static 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}, >+ >+ /* Start with output sum widgets muted and their output gains at min */ >+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x17, 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. >+ */ >+ {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, 0x00}, >+ {0x17, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ >+ /* Mute all inputs to mixer widget (even unconnected ones) */ >+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* Mixer pin */ >+ {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_MODELS >+}; >+ >+static const char *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 >+}; >+ >+static struct snd_pci_quirk cxt5045_cfg_tbl[] = { >+ SND_PCI_QUIRK(0x103c, 0x30a5, "HP", CXT5045_LAPTOP_HPSENSE), >+ SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV Series", CXT5045_LAPTOP_HPSENSE), >+ SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2120", CXT5045_LAPTOP_HPSENSE), >+ SND_PCI_QUIRK(0x103c, 0x30b7, "HP DV6000Z", CXT5045_LAPTOP_HPSENSE), >+ SND_PCI_QUIRK(0x103c, 0x30bb, "HP DV8000", CXT5045_LAPTOP_HPSENSE), >+ SND_PCI_QUIRK(0x103c, 0x30cd, "HP DV Series", CXT5045_LAPTOP_HPSENSE), >+ SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV9533EG", CXT5045_LAPTOP_HPSENSE), >+ SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HP530), >+ SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP_HPSENSE), >+ SND_PCI_QUIRK(0x152d, 0x0753, "Benq R55E", CXT5045_BENQ), >+ SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_LAPTOP_MICSENSE), >+ SND_PCI_QUIRK(0x1734, 0x10cb, "Fujitsu Si3515", CXT5045_LAPTOP_HPMICSENSE), >+ SND_PCI_QUIRK(0x1734, 0x110e, "Fujitsu V5505", CXT5045_LAPTOP_HPSENSE), >+ 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(0x1631, 0xc106, "Packard Bell", CXT5045_LAPTOP_HPMICSENSE), >+ SND_PCI_QUIRK(0x1631, 0xc107, "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; >+ >+ spec = kzalloc(sizeof(*spec), GFP_KERNEL); >+ if (!spec) >+ return -ENOMEM; >+ mutex_init(&spec->amp_mutex); >+ codec->spec = spec; >+ >+ 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, >+ >+ >+ codec->patch_ops = conexant_patch_ops; >+ >+ board_config = snd_hda_check_board_config(codec, CXT5045_MODELS, >+ cxt5045_models, >+ cxt5045_cfg_tbl); >+ 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: >+ 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 >+ } >+ >+ /* >+ * Fix max PCM level to 0 dB >+ * (originall 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)); >+ >+ return 0; >+} >+ >+ >+/* Conexant 5047 specific */ >+#define CXT5047_SPDIF_OUT 0x11 >+ >+static hda_nid_t cxt5047_dac_nids[2] = { 0x10, 0x1c }; >+static hda_nid_t cxt5047_adc_nids[1] = { 0x12 }; >+static hda_nid_t cxt5047_capsrc_nids[1] = { 0x1a }; >+ >+static struct hda_channel_mode cxt5047_modes[1] = { >+ { 2, NULL }, >+}; >+ >+static struct hda_input_mux cxt5047_capture_source = { >+ .num_items = 1, >+ .items = { >+ { "Mic", 0x2 }, >+ } >+}; >+ >+static struct hda_input_mux cxt5047_hp_capture_source = { >+ .num_items = 1, >+ .items = { >+ { "ExtMic", 0x2 }, >+ } >+}; >+ >+static 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; >+ snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0, >+ 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; >+} >+ >+/* bind volumes of both NID 0x13 (Headphones) and 0x1d (Speakers) */ >+static struct hda_bind_ctls cxt5047_bind_master_vol = { >+ .ops = &snd_hda_bind_vol, >+ .values = { >+ HDA_COMPOSE_AMP_VAL(0x13, 3, 0, HDA_OUTPUT), >+ HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT), >+ 0 >+ }, >+}; >+ >+/* 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_codec_read(codec, 0x13, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ >+ bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0; >+ snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, bits); >+ /* Mute/Unmute PCM 2 for good measure - some systems need this */ >+ snd_hda_codec_amp_stereo(codec, 0x1c, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, bits); >+} >+ >+/* mute internal speaker if HP is plugged */ >+static void cxt5047_hp2_automute(struct hda_codec *codec) >+{ >+ struct conexant_spec *spec = codec->spec; >+ unsigned int bits; >+ >+ spec->hp_present = snd_hda_codec_read(codec, 0x13, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ >+ bits = spec->hp_present ? HDA_AMP_MUTE : 0; >+ snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, bits); >+ /* Mute/Unmute PCM 2 for good measure - some systems need this */ >+ snd_hda_codec_amp_stereo(codec, 0x1c, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, bits); >+} >+ >+/* toggle input of built-in and mic jack appropriately */ >+static void cxt5047_hp_automic(struct hda_codec *codec) >+{ >+ static 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 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_codec_read(codec, 0x15, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ 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; >+ } >+} >+ >+/* unsolicited event for HP jack sensing - non-EAPD systems */ >+static void cxt5047_hp2_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ res >>= 26; >+ switch (res) { >+ case CONEXANT_HP_EVENT: >+ cxt5047_hp2_automute(codec); >+ break; >+ case CONEXANT_MIC_EVENT: >+ cxt5047_hp_automic(codec); >+ break; >+ } >+} >+ >+static struct snd_kcontrol_new cxt5047_mixers[] = { >+ HDA_CODEC_VOLUME("Mic Bypass Capture Volume", 0x19, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Bypass Capture Switch", 0x19, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Gain Volume", 0x1a, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Mic Gain Switch", 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), >+ HDA_CODEC_VOLUME("PCM-2 Volume", 0x1c, 0x00, HDA_OUTPUT), >+ HDA_CODEC_MUTE("PCM-2 Switch", 0x1c, 0x00, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x1d, 0x00, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x1d, 0x00, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x13, 0x00, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x13, 0x00, HDA_OUTPUT), >+ >+ {} >+}; >+ >+static struct snd_kcontrol_new cxt5047_toshiba_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 >+ }, >+ HDA_CODEC_VOLUME("Mic Bypass Capture Volume", 0x19, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Bypass Capture Switch", 0x19, 0x02, HDA_INPUT), >+ 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), >+ HDA_BIND_VOL("Master Playback Volume", &cxt5047_bind_master_vol), >+ { >+ .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 struct snd_kcontrol_new cxt5047_hp_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 >+ }, >+ HDA_CODEC_VOLUME("Mic Bypass Capture Volume", 0x19, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Bypass Capture Switch", 0x19,0x02,HDA_INPUT), >+ 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), >+ HDA_CODEC_VOLUME("Master Playback Volume", 0x13, 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, >+ }, >+ { } /* end */ >+}; >+ >+static 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,0x1}, >+ {0x1d, AC_VERB_SET_CONNECT_SEL,0x0}, >+ /* 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 struct hda_verb cxt5047_toshiba_init_verbs[] = { >+ {0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x0 }, /* default on */ >+ /* pin sensing on HP and Mic jacks */ >+ {0x13, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT}, >+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT}, >+ /* Speaker routing */ >+ {0x1d, AC_VERB_SET_CONNECT_SEL,0x1}, >+ {} >+}; >+ >+/* configuration for HP Laptops */ >+static struct hda_verb cxt5047_hp_init_verbs[] = { >+ /* pin sensing on HP jack */ >+ {0x13, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT}, >+ /* 0x13 is actually shared by both HP and speaker; >+ * setting the connection to 0 (=0x19) makes the master volume control >+ * working mysteriouslly... >+ */ >+ {0x13, AC_VERB_SET_CONNECT_SEL, 0x0}, >+ /* Record selector: Ext 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}, >+ /* Speaker routing */ >+ {0x1d, AC_VERB_SET_CONNECT_SEL,0x1}, >+ {} >+}; >+ >+/* Test configuration for debugging, modelled after the ALC260 test >+ * configuration. >+ */ >+#ifdef CONFIG_SND_DEBUG >+static 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 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("Input-1 Volume", 0x1a, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Input-1 Switch", 0x1a, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Input-2 Volume", 0x1a, 0x1, HDA_INPUT), >+ HDA_CODEC_MUTE("Input-2 Switch", 0x1a, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("Input-3 Volume", 0x1a, 0x2, HDA_INPUT), >+ HDA_CODEC_MUTE("Input-3 Switch", 0x1a, 0x2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Input-4 Volume", 0x1a, 0x3, HDA_INPUT), >+ HDA_CODEC_MUTE("Input-4 Switch", 0x1a, 0x3, HDA_INPUT), >+ HDA_CODEC_VOLUME("Input-5 Volume", 0x1a, 0x4, HDA_INPUT), >+ HDA_CODEC_MUTE("Input-5 Switch", 0x1a, 0x4, HDA_INPUT), >+ >+ { } /* end */ >+}; >+ >+static 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_MODELS >+}; >+ >+static const char *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 >+}; >+ >+static struct snd_pci_quirk cxt5047_cfg_tbl[] = { >+ SND_PCI_QUIRK(0x103c, 0x30a0, "HP DV1000", CXT5047_LAPTOP), >+ SND_PCI_QUIRK(0x103c, 0x30a5, "HP DV5200T/DV8000T", CXT5047_LAPTOP_HP), >+ SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV2000T/DV3000T", CXT5047_LAPTOP), >+ SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2000Z", 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; >+ >+ spec = kzalloc(sizeof(*spec), GFP_KERNEL); >+ if (!spec) >+ return -ENOMEM; >+ mutex_init(&spec->amp_mutex); >+ codec->spec = spec; >+ >+ 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->input_mux = &cxt5047_capture_source; >+ spec->num_mixers = 1; >+ spec->mixers[0] = cxt5047_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; >+ >+ board_config = snd_hda_check_board_config(codec, CXT5047_MODELS, >+ cxt5047_models, >+ cxt5047_cfg_tbl); >+ switch (board_config) { >+ case CXT5047_LAPTOP: >+ codec->patch_ops.unsol_event = cxt5047_hp2_unsol_event; >+ break; >+ case CXT5047_LAPTOP_HP: >+ spec->input_mux = &cxt5047_hp_capture_source; >+ spec->num_init_verbs = 2; >+ spec->init_verbs[1] = cxt5047_hp_init_verbs; >+ spec->mixers[0] = cxt5047_hp_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_init_verbs = 2; >+ spec->init_verbs[1] = cxt5047_toshiba_init_verbs; >+ spec->mixers[0] = cxt5047_toshiba_mixers; >+ 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 >+ } >+ return 0; >+} >+ >+/* Conexant 5051 specific */ >+static hda_nid_t cxt5051_dac_nids[1] = { 0x10 }; >+static hda_nid_t cxt5051_adc_nids[2] = { 0x14, 0x15 }; >+#define CXT5051_SPDIF_OUT 0x1C >+#define CXT5051_PORTB_EVENT 0x38 >+#define CXT5051_PORTC_EVENT 0x39 >+ >+static 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; >+ pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0; >+ snd_hda_codec_write(codec, 0x1a, 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) >+{ >+ unsigned int present; >+ >+ present = snd_hda_codec_read(codec, 0x17, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & >+ AC_PINSENSE_PRESENCE; >+ 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; >+ >+ present = snd_hda_codec_read(codec, 0x18, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & >+ AC_PINSENSE_PRESENCE; >+ 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); >+ 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_codec_read(codec, 0x16, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & >+ AC_PINSENSE_PRESENCE; >+ 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 struct snd_kcontrol_new cxt5051_mixers[] = { >+ HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT), >+ HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT), >+ HDA_CODEC_VOLUME("External Mic Volume", 0x14, 0x01, HDA_INPUT), >+ HDA_CODEC_MUTE("External Mic Switch", 0x14, 0x01, HDA_INPUT), >+ HDA_CODEC_VOLUME("Docking Mic Volume", 0x15, 0x00, HDA_INPUT), >+ HDA_CODEC_MUTE("Docking Mic Switch", 0x15, 0x00, HDA_INPUT), >+ HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Master Playback Switch", >+ .info = cxt_eapd_info, >+ .get = cxt_eapd_get, >+ .put = cxt5051_hp_master_sw_put, >+ .private_value = 0x1a, >+ }, >+ >+ {} >+}; >+ >+static struct snd_kcontrol_new cxt5051_hp_mixers[] = { >+ HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT), >+ HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT), >+ HDA_CODEC_VOLUME("External Mic Volume", 0x15, 0x00, HDA_INPUT), >+ HDA_CODEC_MUTE("External Mic Switch", 0x15, 0x00, HDA_INPUT), >+ HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Master Playback Switch", >+ .info = cxt_eapd_info, >+ .get = cxt_eapd_get, >+ .put = cxt5051_hp_master_sw_put, >+ .private_value = 0x1a, >+ }, >+ >+ {} >+}; >+ >+static struct 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: Int 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_CONNECT_SEL, 0x0}, >+ /* EAPD */ >+ {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ >+ {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT}, >+ {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT}, >+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTC_EVENT}, >+ { } /* end */ >+}; >+ >+/* initialize jack-sensing, too */ >+static int cxt5051_init(struct hda_codec *codec) >+{ >+ conexant_init(codec); >+ 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_MODELS >+}; >+ >+static const char *cxt5051_models[CXT5051_MODELS] = { >+ [CXT5051_LAPTOP] = "laptop", >+ [CXT5051_HP] = "hp", >+}; >+ >+static struct snd_pci_quirk cxt5051_cfg_tbl[] = { >+ SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board", >+ CXT5051_LAPTOP), >+ SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP), >+ {} >+}; >+ >+static int patch_cxt5051(struct hda_codec *codec) >+{ >+ struct conexant_spec *spec; >+ int board_config; >+ >+ spec = kzalloc(sizeof(*spec), GFP_KERNEL); >+ if (!spec) >+ return -ENOMEM; >+ mutex_init(&spec->amp_mutex); >+ codec->spec = spec; >+ >+ 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 = 1; >+ spec->mixers[0] = cxt5051_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; >+ >+ board_config = snd_hda_check_board_config(codec, CXT5051_MODELS, >+ cxt5051_models, >+ cxt5051_cfg_tbl); >+ switch (board_config) { >+ case CXT5051_HP: >+ codec->patch_ops.unsol_event = cxt5051_hp_unsol_event; >+ spec->mixers[0] = cxt5051_hp_mixers; >+ break; >+ default: >+ case CXT5051_LAPTOP: >+ codec->patch_ops.unsol_event = cxt5051_hp_unsol_event; >+ break; >+ } >+ >+ return 0; >+} >+ >+ >+/* >+ */ >+ >+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 }, >+ {} /* terminator */ >+}; >diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c >new file mode 100644 >index 0000000..7613e37 >--- /dev/null >+++ b/sound/pci/hda/patch_realtek.c >@@ -0,0 +1,14665 @@ >+/* >+ * Universal Interface for Intel High Definition Audio Codec >+ * >+ * HD audio interface patch for ALC 260/880/882 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 <sound/driver.h> >+#include <linux/init.h> >+#include <linux/delay.h> >+#include <linux/slab.h> >+#include <linux/pci.h> >+#include <sound/core.h> >+#include "hda_codec.h" >+#include "hda_local.h" >+#include "hda_patch.h" >+ >+#define ALC880_FRONT_EVENT 0x01 >+#define ALC880_DCVOL_EVENT 0x02 >+#define ALC880_HP_EVENT 0x04 >+#define ALC880_MIC_EVENT 0x08 >+ >+/* ALC880 board config type */ >+enum { >+ ALC880_3ST, >+ ALC880_3ST_DIG, >+ ALC880_5ST, >+ ALC880_5ST_DIG, >+ ALC880_W810, >+ ALC880_Z71V, >+ ALC880_6ST, >+ ALC880_6ST_DIG, >+ ALC880_F1734, >+ ALC880_ASUS, >+ ALC880_ASUS_DIG, >+ ALC880_ASUS_W1V, >+ ALC880_ASUS_DIG2, >+ ALC880_FUJITSU, >+ ALC880_UNIWILL_DIG, >+ ALC880_UNIWILL, >+ ALC880_UNIWILL_P53, >+ ALC880_CLEVO, >+ ALC880_TCL_S700, >+ ALC880_LG, >+ ALC880_LG_LW, >+ ALC880_MEDION_RIM, >+#ifdef CONFIG_SND_DEBUG >+ ALC880_TEST, >+#endif >+ ALC880_AUTO, >+ ALC880_MODEL_LAST /* last tag */ >+}; >+ >+/* ALC260 models */ >+enum { >+ ALC260_BASIC, >+ ALC260_HP, >+ ALC260_HP_3013, >+ ALC260_FUJITSU_S702X, >+ ALC260_ACER, >+ ALC260_WILL, >+ ALC260_REPLACER_672V, >+#ifdef CONFIG_SND_DEBUG >+ ALC260_TEST, >+#endif >+ ALC260_AUTO, >+ ALC260_MODEL_LAST /* last tag */ >+}; >+ >+/* ALC262 models */ >+enum { >+ ALC262_BASIC, >+ ALC262_HIPPO, >+ ALC262_HIPPO_1, >+ ALC262_FUJITSU, >+ ALC262_HP_BPC, >+ ALC262_HP_BPC_D7000_WL, >+ ALC262_HP_BPC_D7000_WF, >+ ALC262_HP_TC_T5735, >+ ALC262_HP_RP5700, >+ ALC262_BENQ_ED8, >+ ALC262_SONY_ASSAMD, >+ ALC262_BENQ_T31, >+ ALC262_ULTRA, >+ ALC262_LENOVO_3000, >+ ALC262_AUTO, >+ ALC262_MODEL_LAST /* last tag */ >+}; >+ >+/* ALC268 models */ >+enum { >+ ALC267_QUANTA_IL1, >+ ALC268_3ST, >+ ALC268_TOSHIBA, >+ ALC268_ACER, >+ ALC268_DELL, >+ ALC268_ZEPTO, >+#ifdef CONFIG_SND_DEBUG >+ ALC268_TEST, >+#endif >+ ALC268_AUTO, >+ ALC268_MODEL_LAST /* last tag */ >+}; >+ >+/* ALC269 models */ >+enum { >+ ALC269_BASIC, >+ ALC269_AUTO, >+ ALC269_MODEL_LAST /* last tag */ >+}; >+ >+/* ALC861 models */ >+enum { >+ ALC861_3ST, >+ ALC660_3ST, >+ ALC861_3ST_DIG, >+ ALC861_6ST_DIG, >+ ALC861_UNIWILL_M31, >+ ALC861_TOSHIBA, >+ ALC861_ASUS, >+ ALC861_ASUS_LAPTOP, >+ ALC861_AUTO, >+ ALC861_MODEL_LAST, >+}; >+ >+/* ALC861-VD models */ >+enum { >+ ALC660VD_3ST, >+ ALC660VD_3ST_DIG, >+ ALC861VD_3ST, >+ ALC861VD_3ST_DIG, >+ ALC861VD_6ST_DIG, >+ ALC861VD_LENOVO, >+ ALC861VD_DALLAS, >+ ALC861VD_HP, >+ ALC861VD_AUTO, >+ ALC861VD_MODEL_LAST, >+}; >+ >+/* ALC662 models */ >+enum { >+ ALC662_3ST_2ch_DIG, >+ ALC662_3ST_6ch_DIG, >+ ALC662_3ST_6ch, >+ ALC662_5ST_DIG, >+ ALC662_LENOVO_101E, >+ ALC662_ASUS_EEEPC_P701, >+ ALC662_ASUS_EEEPC_EP20, >+ ALC663_ASUS_M51VA, >+ ALC663_ASUS_G71V, >+ ALC663_ASUS_H13, >+ ALC663_ASUS_G50V, >+ ALC662_AUTO, >+ ALC662_MODEL_LAST, >+}; >+ >+/* ALC882 models */ >+enum { >+ ALC882_3ST_DIG, >+ ALC882_6ST_DIG, >+ ALC882_ARIMA, >+ ALC882_W2JC, >+ ALC882_TARGA, >+ ALC882_ASUS_A7J, >+ ALC882_ASUS_A7M, >+ ALC885_MACPRO, >+ ALC885_MBP3, >+ ALC885_IMAC24, >+ ALC882_AUTO, >+ ALC882_MODEL_LAST, >+}; >+ >+/* ALC883 models */ >+enum { >+ ALC883_3ST_2ch_DIG, >+ ALC883_3ST_6ch_DIG, >+ ALC883_3ST_6ch, >+ ALC883_6ST_DIG, >+ ALC883_TARGA_DIG, >+ ALC883_TARGA_2ch_DIG, >+ ALC883_ACER, >+ ALC883_ACER_ASPIRE, >+ ALC883_MEDION, >+ ALC883_MEDION_MD2, >+ ALC883_LAPTOP_EAPD, >+ ALC883_LENOVO_101E_2ch, >+ ALC883_LENOVO_NB0763, >+ ALC888_LENOVO_MS7195_DIG, >+ ALC883_HAIER_W66, >+ ALC888_3ST_HP, >+ ALC888_6ST_DELL, >+ ALC883_MITAC, >+ ALC883_CLEVO_M720, >+ ALC883_FUJITSU_PI2515, >+ ALC883_3ST_6ch_INTEL, >+ ALC883_AUTO, >+ ALC883_MODEL_LAST, >+}; >+ >+/* for GPIO Poll */ >+#define GPIO_MASK 0x03 >+ >+struct alc_spec { >+ /* codec parameterization */ >+ struct snd_kcontrol_new *mixers[5]; /* mixer arrays */ >+ unsigned int num_mixers; >+ >+ const struct hda_verb *init_verbs[5]; /* initialization verbs >+ * don't forget NULL >+ * termination! >+ */ >+ unsigned int num_init_verbs; >+ >+ char *stream_name_analog; /* analog PCM stream */ >+ struct hda_pcm_stream *stream_analog_playback; >+ struct hda_pcm_stream *stream_analog_capture; >+ struct hda_pcm_stream *stream_analog_alt_playback; >+ struct hda_pcm_stream *stream_analog_alt_capture; >+ >+ char *stream_name_digital; /* digital PCM stream */ >+ struct hda_pcm_stream *stream_digital_playback; >+ 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; >+ >+ /* capture */ >+ unsigned int num_adc_nids; >+ hda_nid_t *adc_nids; >+ hda_nid_t *capsrc_nids; >+ hda_nid_t dig_in_nid; /* digital-in NID; optional */ >+ >+ /* capture source */ >+ unsigned int num_mux_defs; >+ const struct hda_input_mux *input_mux; >+ unsigned int cur_mux[3]; >+ >+ /* channel model */ >+ const struct hda_channel_mode *channel_mode; >+ int num_channel_mode; >+ int need_dac_fix; >+ >+ /* 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; >+ unsigned int num_kctl_alloc, num_kctl_used; >+ struct snd_kcontrol_new *kctl_alloc; >+ struct hda_input_mux private_imux; >+ hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; >+ >+ /* hooks */ >+ void (*init_hook)(struct hda_codec *codec); >+ void (*unsol_event)(struct hda_codec *codec, unsigned int res); >+ >+ /* for pin sensing */ >+ unsigned int sense_updated: 1; >+ unsigned int jack_present: 1; >+ unsigned int master_sw: 1; >+ >+ /* for virtual master */ >+ hda_nid_t vmaster_nid; >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+ struct hda_loopback_check loopback; >+#endif >+ >+ /* for PLL fix */ >+ hda_nid_t pll_nid; >+ unsigned int pll_coef_idx, pll_coef_bit; >+}; >+ >+/* >+ * configuration template - to be copied to the spec instance >+ */ >+struct alc_config_preset { >+ struct snd_kcontrol_new *mixers[5]; /* should be identical size >+ * with spec >+ */ >+ const struct hda_verb *init_verbs[5]; >+ unsigned int num_dacs; >+ hda_nid_t *dac_nids; >+ hda_nid_t dig_out_nid; /* optional */ >+ hda_nid_t hp_nid; /* optional */ >+ unsigned int num_adc_nids; >+ hda_nid_t *adc_nids; >+ hda_nid_t *capsrc_nids; >+ hda_nid_t dig_in_nid; >+ unsigned int num_channel_mode; >+ const struct hda_channel_mode *channel_mode; >+ int need_dac_fix; >+ unsigned int num_mux_defs; >+ const struct hda_input_mux *input_mux; >+ void (*unsol_event)(struct hda_codec *, unsigned int); >+ void (*init_hook)(struct hda_codec *); >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+ struct hda_amp_list *loopbacks; >+#endif >+}; >+ >+ >+/* >+ * 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; >+ 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 int alc_mux_enum_put(struct snd_kcontrol *kcontrol, >+ struct snd_ctl_elem_value *ucontrol) >+{ >+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); >+ struct alc_spec *spec = codec->spec; >+ unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); >+ unsigned int mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx; >+ hda_nid_t nid = spec->capsrc_nids ? >+ spec->capsrc_nids[adc_idx] : spec->adc_nids[adc_idx]; >+ return snd_hda_input_mux_put(codec, &spec->input_mux[mux_idx], ucontrol, >+ nid, &spec->cur_mux[adc_idx]); >+} >+ >+ >+/* >+ * channel mode setting >+ */ >+static int alc_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; >+ return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode, >+ spec->num_channel_mode); >+} >+ >+static int alc_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; >+ return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode, >+ spec->num_channel_mode, >+ spec->multiout.max_channels); >+} >+ >+static int alc_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 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; >+} >+ >+/* >+ * Control the mode of pin widget settings via the mixer. "pc" is used >+ * instead of "%" to avoid consequences of accidently treating the % as >+ * being part of a format specifier. Maximum allowed length of a value is >+ * 63 characters plus NULL terminator. >+ * >+ * Note: some retasking pin complexes seem to ignore requests for input >+ * states other than HiZ (eg: PIN_VREFxx) and revert to HiZ if any of these >+ * are requested. Therefore order this list so that this behaviour will not >+ * cause problems when mixer clients move through the enum sequentially. >+ * NIDs 0x0f and 0x10 have been observed to have this behaviour as of >+ * March 2006. >+ */ >+static char *alc_pin_mode_names[] = { >+ "Mic 50pc bias", "Mic 80pc bias", >+ "Line in", "Line out", "Headphone out", >+}; >+static unsigned char alc_pin_mode_values[] = { >+ PIN_VREF50, PIN_VREF80, PIN_IN, PIN_OUT, PIN_HP, >+}; >+/* The control can present all 5 options, or it can limit the options based >+ * in the pin being assumed to be exclusively an input or an output pin. In >+ * addition, "input" pins may or may not process the mic bias option >+ * depending on actual widget capability (NIDs 0x0f and 0x10 don't seem to >+ * accept requests for bias as of chip versions up to March 2006) and/or >+ * wiring in the computer. >+ */ >+#define ALC_PIN_DIR_IN 0x00 >+#define ALC_PIN_DIR_OUT 0x01 >+#define ALC_PIN_DIR_INOUT 0x02 >+#define ALC_PIN_DIR_IN_NOMICBIAS 0x03 >+#define ALC_PIN_DIR_INOUT_NOMICBIAS 0x04 >+ >+/* Info about the pin modes supported by the different pin direction modes. >+ * For each direction the minimum and maximum values are given. >+ */ >+static signed char alc_pin_mode_dir_info[5][2] = { >+ { 0, 2 }, /* ALC_PIN_DIR_IN */ >+ { 3, 4 }, /* ALC_PIN_DIR_OUT */ >+ { 0, 4 }, /* ALC_PIN_DIR_INOUT */ >+ { 2, 2 }, /* ALC_PIN_DIR_IN_NOMICBIAS */ >+ { 2, 4 }, /* ALC_PIN_DIR_INOUT_NOMICBIAS */ >+}; >+#define alc_pin_mode_min(_dir) (alc_pin_mode_dir_info[_dir][0]) >+#define alc_pin_mode_max(_dir) (alc_pin_mode_dir_info[_dir][1]) >+#define alc_pin_mode_n_items(_dir) \ >+ (alc_pin_mode_max(_dir)-alc_pin_mode_min(_dir)+1) >+ >+static int alc_pin_mode_info(struct snd_kcontrol *kcontrol, >+ struct snd_ctl_elem_info *uinfo) >+{ >+ unsigned int item_num = uinfo->value.enumerated.item; >+ unsigned char dir = (kcontrol->private_value >> 16) & 0xff; >+ >+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; >+ uinfo->count = 1; >+ uinfo->value.enumerated.items = alc_pin_mode_n_items(dir); >+ >+ if (item_num<alc_pin_mode_min(dir) || item_num>alc_pin_mode_max(dir)) >+ item_num = alc_pin_mode_min(dir); >+ strcpy(uinfo->value.enumerated.name, alc_pin_mode_names[item_num]); >+ return 0; >+} >+ >+static int alc_pin_mode_get(struct snd_kcontrol *kcontrol, >+ struct snd_ctl_elem_value *ucontrol) >+{ >+ unsigned int i; >+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); >+ hda_nid_t nid = kcontrol->private_value & 0xffff; >+ unsigned char dir = (kcontrol->private_value >> 16) & 0xff; >+ long *valp = ucontrol->value.integer.value; >+ unsigned int pinctl = snd_hda_codec_read(codec, nid, 0, >+ AC_VERB_GET_PIN_WIDGET_CONTROL, >+ 0x00); >+ >+ /* Find enumerated value for current pinctl setting */ >+ i = alc_pin_mode_min(dir); >+ while (alc_pin_mode_values[i] != pinctl && i <= alc_pin_mode_max(dir)) >+ i++; >+ *valp = i <= alc_pin_mode_max(dir) ? i: alc_pin_mode_min(dir); >+ return 0; >+} >+ >+static int alc_pin_mode_put(struct snd_kcontrol *kcontrol, >+ struct snd_ctl_elem_value *ucontrol) >+{ >+ signed int change; >+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); >+ hda_nid_t nid = kcontrol->private_value & 0xffff; >+ unsigned char dir = (kcontrol->private_value >> 16) & 0xff; >+ long val = *ucontrol->value.integer.value; >+ unsigned int pinctl = snd_hda_codec_read(codec, nid, 0, >+ AC_VERB_GET_PIN_WIDGET_CONTROL, >+ 0x00); >+ >+ if (val < alc_pin_mode_min(dir) || val > alc_pin_mode_max(dir)) >+ val = alc_pin_mode_min(dir); >+ >+ change = pinctl != alc_pin_mode_values[val]; >+ if (change) { >+ /* Set pin mode to that requested */ >+ snd_hda_codec_write_cache(codec, nid, 0, >+ AC_VERB_SET_PIN_WIDGET_CONTROL, >+ alc_pin_mode_values[val]); >+ >+ /* Also enable the retasking pin's input/output as required >+ * for the requested pin mode. Enum values of 2 or less are >+ * input modes. >+ * >+ * Dynamically switching the input/output buffers probably >+ * reduces noise slightly (particularly on input) so we'll >+ * do it. However, having both input and output buffers >+ * enabled simultaneously doesn't seem to be problematic if >+ * this turns out to be necessary in the future. >+ */ >+ if (val <= 2) { >+ snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, HDA_AMP_MUTE); >+ snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 0, >+ HDA_AMP_MUTE, 0); >+ } else { >+ snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 0, >+ HDA_AMP_MUTE, HDA_AMP_MUTE); >+ snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, 0); >+ } >+ } >+ return change; >+} >+ >+#define ALC_PIN_MODE(xname, nid, dir) \ >+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ >+ .info = alc_pin_mode_info, \ >+ .get = alc_pin_mode_get, \ >+ .put = alc_pin_mode_put, \ >+ .private_value = nid | (dir<<16) } >+ >+/* A switch control for ALC260 GPIO pins. Multiple GPIOs can be ganged >+ * together using a mask with more than one bit set. This control is >+ * currently used only by the ALC260 test model. At this stage they are not >+ * needed for any "production" models. >+ */ >+#ifdef CONFIG_SND_DEBUG >+#define alc_gpio_data_info snd_ctl_boolean_mono_info >+ >+static int alc_gpio_data_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 & 0xffff; >+ unsigned char mask = (kcontrol->private_value >> 16) & 0xff; >+ long *valp = ucontrol->value.integer.value; >+ unsigned int val = snd_hda_codec_read(codec, nid, 0, >+ AC_VERB_GET_GPIO_DATA, 0x00); >+ >+ *valp = (val & mask) != 0; >+ return 0; >+} >+static int alc_gpio_data_put(struct snd_kcontrol *kcontrol, >+ struct snd_ctl_elem_value *ucontrol) >+{ >+ signed int change; >+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); >+ hda_nid_t nid = kcontrol->private_value & 0xffff; >+ unsigned char mask = (kcontrol->private_value >> 16) & 0xff; >+ long val = *ucontrol->value.integer.value; >+ unsigned int gpio_data = snd_hda_codec_read(codec, nid, 0, >+ AC_VERB_GET_GPIO_DATA, >+ 0x00); >+ >+ /* Set/unset the masked GPIO bit(s) as needed */ >+ change = (val == 0 ? 0 : mask) != (gpio_data & mask); >+ if (val == 0) >+ gpio_data &= ~mask; >+ else >+ gpio_data |= mask; >+ snd_hda_codec_write_cache(codec, nid, 0, >+ AC_VERB_SET_GPIO_DATA, gpio_data); >+ >+ return change; >+} >+#define ALC_GPIO_DATA_SWITCH(xname, nid, mask) \ >+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ >+ .info = alc_gpio_data_info, \ >+ .get = alc_gpio_data_get, \ >+ .put = alc_gpio_data_put, \ >+ .private_value = nid | (mask<<16) } >+#endif /* CONFIG_SND_DEBUG */ >+ >+/* A switch control to allow the enabling of the digital IO pins on the >+ * ALC260. This is incredibly simplistic; the intention of this control is >+ * to provide something in the test model allowing digital outputs to be >+ * identified if present. If models are found which can utilise these >+ * outputs a more complete mixer control can be devised for those models if >+ * necessary. >+ */ >+#ifdef CONFIG_SND_DEBUG >+#define alc_spdif_ctrl_info snd_ctl_boolean_mono_info >+ >+static int alc_spdif_ctrl_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 & 0xffff; >+ unsigned char mask = (kcontrol->private_value >> 16) & 0xff; >+ long *valp = ucontrol->value.integer.value; >+ unsigned int val = snd_hda_codec_read(codec, nid, 0, >+ AC_VERB_GET_DIGI_CONVERT_1, 0x00); >+ >+ *valp = (val & mask) != 0; >+ return 0; >+} >+static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol, >+ struct snd_ctl_elem_value *ucontrol) >+{ >+ signed int change; >+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); >+ hda_nid_t nid = kcontrol->private_value & 0xffff; >+ unsigned char mask = (kcontrol->private_value >> 16) & 0xff; >+ long val = *ucontrol->value.integer.value; >+ unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0, >+ AC_VERB_GET_DIGI_CONVERT_1, >+ 0x00); >+ >+ /* Set/unset the masked control bit(s) as needed */ >+ change = (val == 0 ? 0 : mask) != (ctrl_data & mask); >+ if (val==0) >+ ctrl_data &= ~mask; >+ else >+ ctrl_data |= mask; >+ snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, >+ ctrl_data); >+ >+ return change; >+} >+#define ALC_SPDIF_CTRL_SWITCH(xname, nid, mask) \ >+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ >+ .info = alc_spdif_ctrl_info, \ >+ .get = alc_spdif_ctrl_get, \ >+ .put = alc_spdif_ctrl_put, \ >+ .private_value = nid | (mask<<16) } >+#endif /* CONFIG_SND_DEBUG */ >+ >+/* A switch control to allow the enabling EAPD digital outputs on the ALC26x. >+ * Again, this is only used in the ALC26x test models to help identify when >+ * the EAPD line must be asserted for features to work. >+ */ >+#ifdef CONFIG_SND_DEBUG >+#define alc_eapd_ctrl_info snd_ctl_boolean_mono_info >+ >+static int alc_eapd_ctrl_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 & 0xffff; >+ unsigned char mask = (kcontrol->private_value >> 16) & 0xff; >+ long *valp = ucontrol->value.integer.value; >+ unsigned int val = snd_hda_codec_read(codec, nid, 0, >+ AC_VERB_GET_EAPD_BTLENABLE, 0x00); >+ >+ *valp = (val & mask) != 0; >+ return 0; >+} >+ >+static int alc_eapd_ctrl_put(struct snd_kcontrol *kcontrol, >+ struct snd_ctl_elem_value *ucontrol) >+{ >+ int change; >+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); >+ hda_nid_t nid = kcontrol->private_value & 0xffff; >+ unsigned char mask = (kcontrol->private_value >> 16) & 0xff; >+ long val = *ucontrol->value.integer.value; >+ unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0, >+ AC_VERB_GET_EAPD_BTLENABLE, >+ 0x00); >+ >+ /* Set/unset the masked control bit(s) as needed */ >+ change = (!val ? 0 : mask) != (ctrl_data & mask); >+ if (!val) >+ ctrl_data &= ~mask; >+ else >+ ctrl_data |= mask; >+ snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE, >+ ctrl_data); >+ >+ return change; >+} >+ >+#define ALC_EAPD_CTRL_SWITCH(xname, nid, mask) \ >+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ >+ .info = alc_eapd_ctrl_info, \ >+ .get = alc_eapd_ctrl_get, \ >+ .put = alc_eapd_ctrl_put, \ >+ .private_value = nid | (mask<<16) } >+#endif /* CONFIG_SND_DEBUG */ >+ >+/* >+ * set up from the preset table >+ */ >+static void setup_preset(struct alc_spec *spec, >+ const struct alc_config_preset *preset) >+{ >+ int i; >+ >+ for (i = 0; i < ARRAY_SIZE(preset->mixers) && preset->mixers[i]; i++) >+ spec->mixers[spec->num_mixers++] = preset->mixers[i]; >+ for (i = 0; i < ARRAY_SIZE(preset->init_verbs) && preset->init_verbs[i]; >+ i++) >+ spec->init_verbs[spec->num_init_verbs++] = >+ preset->init_verbs[i]; >+ >+ spec->channel_mode = preset->channel_mode; >+ spec->num_channel_mode = preset->num_channel_mode; >+ spec->need_dac_fix = preset->need_dac_fix; >+ >+ spec->multiout.max_channels = spec->channel_mode[0].channels; >+ >+ spec->multiout.num_dacs = preset->num_dacs; >+ spec->multiout.dac_nids = preset->dac_nids; >+ spec->multiout.dig_out_nid = preset->dig_out_nid; >+ spec->multiout.hp_nid = preset->hp_nid; >+ >+ spec->num_mux_defs = preset->num_mux_defs; >+ if (!spec->num_mux_defs) >+ spec->num_mux_defs = 1; >+ spec->input_mux = preset->input_mux; >+ >+ spec->num_adc_nids = preset->num_adc_nids; >+ spec->adc_nids = preset->adc_nids; >+ spec->capsrc_nids = preset->capsrc_nids; >+ spec->dig_in_nid = preset->dig_in_nid; >+ >+ spec->unsol_event = preset->unsol_event; >+ spec->init_hook = preset->init_hook; >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+ spec->loopback.amplist = preset->loopbacks; >+#endif >+} >+ >+/* Enable GPIO mask and set output */ >+static 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 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 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); >+} >+ >+static void alc_sku_automute(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ unsigned int present; >+ unsigned int hp_nid = spec->autocfg.hp_pins[0]; >+ unsigned int sp_nid = spec->autocfg.speaker_pins[0]; >+ >+ /* need to execute and sync at first */ >+ snd_hda_codec_read(codec, hp_nid, 0, AC_VERB_SET_PIN_SENSE, 0); >+ present = snd_hda_codec_read(codec, hp_nid, 0, >+ AC_VERB_GET_PIN_SENSE, 0); >+ spec->jack_present = (present & 0x80000000) != 0; >+ snd_hda_codec_write(codec, sp_nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, >+ spec->jack_present ? 0 : PIN_OUT); >+} >+ >+/* unsolicited event for HP jack sensing */ >+static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res) >+{ >+ if (codec->vendor_id == 0x10ec0880) >+ res >>= 28; >+ else >+ res >>= 26; >+ if (res != ALC880_HP_EVENT) >+ return; >+ >+ alc_sku_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) == 2) >+ /* 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); >+} >+ >+/* 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 void alc_subsystem_id(struct hda_codec *codec, >+ unsigned int porta, unsigned int porte, >+ unsigned int portd) >+{ >+ unsigned int ass, tmp, i; >+ unsigned nid; >+ struct alc_spec *spec = codec->spec; >+ >+ ass = codec->subsystem_id & 0xffff; >+ if ((ass != codec->bus->pci->subsystem_device) && (ass & 1)) >+ goto do_sku; >+ >+ /* >+ * 31~30 : port conetcivity >+ * 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_read(codec, nid, 0, >+ AC_VERB_GET_CONFIG_DEFAULT, 0); >+ if (!(ass & 1) && !(ass & 0x100000)) >+ return; >+ if ((ass >> 30) != 1) /* no physical connection */ >+ return; >+ >+ /* check sum */ >+ tmp = 0; >+ for (i = 1; i < 16; i++) { >+ if ((ass >> i) & 1) >+ tmp++; >+ } >+ if (((ass >> 16) & 0xf) != tmp) >+ return; >+do_sku: >+ /* >+ * 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: >+ snd_hda_sequence_write(codec, alc_gpio1_init_verbs); >+ break; >+ case 3: >+ snd_hda_sequence_write(codec, alc_gpio2_init_verbs); >+ break; >+ case 7: >+ snd_hda_sequence_write(codec, alc_gpio3_init_verbs); >+ break; >+ case 5: /* set EAPD output high */ >+ switch (codec->vendor_id) { >+ case 0x10ec0260: >+ snd_hda_codec_write(codec, 0x0f, 0, >+ AC_VERB_SET_EAPD_BTLENABLE, 2); >+ snd_hda_codec_write(codec, 0x10, 0, >+ AC_VERB_SET_EAPD_BTLENABLE, 2); >+ break; >+ case 0x10ec0262: >+ case 0x10ec0267: >+ case 0x10ec0268: >+ case 0x10ec0269: >+ case 0x10ec0660: >+ case 0x10ec0662: >+ case 0x10ec0663: >+ case 0x10ec0862: >+ case 0x10ec0889: >+ snd_hda_codec_write(codec, 0x14, 0, >+ AC_VERB_SET_EAPD_BTLENABLE, 2); >+ snd_hda_codec_write(codec, 0x15, 0, >+ AC_VERB_SET_EAPD_BTLENABLE, 2); >+ break; >+ } >+ 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 0x10ec0889: >+ 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); >+ break; >+ case 0x10ec0888: >+ alc888_coef_init(codec); >+ break; >+ 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; >+ } >+ 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; >+ /* >+ * 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.speaker_pins[0]) { >+ if (spec->autocfg.line_out_pins[0]) >+ spec->autocfg.speaker_pins[0] = >+ spec->autocfg.line_out_pins[0]; >+ else >+ return; >+ } >+ >+ if (!spec->autocfg.hp_pins[0]) { >+ tmp = (ass >> 11) & 0x3; /* HP to chassis */ >+ if (tmp == 0) >+ spec->autocfg.hp_pins[0] = porta; >+ else if (tmp == 1) >+ spec->autocfg.hp_pins[0] = porte; >+ else if (tmp == 2) >+ spec->autocfg.hp_pins[0] = portd; >+ else >+ return; >+ } >+ >+ snd_hda_codec_write(codec, spec->autocfg.hp_pins[0], 0, >+ AC_VERB_SET_UNSOLICITED_ENABLE, >+ AC_USRSP_EN | ALC880_HP_EVENT); >+ spec->unsol_event = alc_sku_unsol_event; >+} >+ >+/* >+ * Fix-up pin default configurations >+ */ >+ >+struct alc_pincfg { >+ hda_nid_t nid; >+ u32 val; >+}; >+ >+static void alc_fix_pincfg(struct hda_codec *codec, >+ const struct snd_pci_quirk *quirk, >+ const struct alc_pincfg **pinfix) >+{ >+ const struct alc_pincfg *cfg; >+ >+ quirk = snd_pci_quirk_lookup(codec->bus->pci, quirk); >+ if (!quirk) >+ return; >+ >+ cfg = pinfix[quirk->value]; >+ for (; cfg->nid; cfg++) { >+ int i; >+ u32 val = cfg->val; >+ for (i = 0; i < 4; i++) { >+ snd_hda_codec_write(codec, cfg->nid, 0, >+ AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 + i, >+ val & 0xff); >+ val >>= 8; >+ } >+ } >+} >+ >+/* >+ * ALC880 3-stack model >+ * >+ * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0e) >+ * Pin assignment: Front = 0x14, Line-In/Surr = 0x1a, Mic/CLFE = 0x18, >+ * F-Mic = 0x1b, HP = 0x19 >+ */ >+ >+static hda_nid_t alc880_dac_nids[4] = { >+ /* front, rear, clfe, rear_surr */ >+ 0x02, 0x05, 0x04, 0x03 >+}; >+ >+static hda_nid_t alc880_adc_nids[3] = { >+ /* ADC0-2 */ >+ 0x07, 0x08, 0x09, >+}; >+ >+/* The datasheet says the node 0x07 is connected from inputs, >+ * but it shows zero connection in the real implementation on some devices. >+ * Note: this is a 915GAV bug, fixed on 915GLV >+ */ >+static hda_nid_t alc880_adc_nids_alt[2] = { >+ /* ADC1-2 */ >+ 0x08, 0x09, >+}; >+ >+#define ALC880_DIGOUT_NID 0x06 >+#define ALC880_DIGIN_NID 0x0a >+ >+static struct hda_input_mux alc880_capture_source = { >+ .num_items = 4, >+ .items = { >+ { "Mic", 0x0 }, >+ { "Front Mic", 0x3 }, >+ { "Line", 0x2 }, >+ { "CD", 0x4 }, >+ }, >+}; >+ >+/* channel source setting (2/6 channel selection for 3-stack) */ >+/* 2ch mode */ >+static struct hda_verb alc880_threestack_ch2_init[] = { >+ /* set line-in to input, mute it */ >+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, >+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, >+ /* set mic-in to input vref 80%, mute it */ >+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, >+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, >+ { } /* end */ >+}; >+ >+/* 6ch mode */ >+static struct hda_verb alc880_threestack_ch6_init[] = { >+ /* set line-in to output, unmute it */ >+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, >+ /* set mic-in to output, unmute it */ >+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, >+ { } /* end */ >+}; >+ >+static struct hda_channel_mode alc880_threestack_modes[2] = { >+ { 2, alc880_threestack_ch2_init }, >+ { 6, alc880_threestack_ch6_init }, >+}; >+ >+static struct snd_kcontrol_new alc880_three_stack_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x0f, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Surround Playback Switch", 0x0f, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), >+ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x3, HDA_INPUT), >+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x3, HDA_INPUT), >+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), >+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x19, 0x0, HDA_OUTPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Channel Mode", >+ .info = alc_ch_mode_info, >+ .get = alc_ch_mode_get, >+ .put = alc_ch_mode_put, >+ }, >+ { } /* end */ >+}; >+ >+/* capture mixer elements */ >+static struct snd_kcontrol_new alc880_capture_mixer[] = { >+ HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ /* The multiple "Capture Source" controls confuse alsamixer >+ * So call somewhat different.. >+ */ >+ /* .name = "Capture Source", */ >+ .name = "Input Source", >+ .count = 3, >+ .info = alc_mux_enum_info, >+ .get = alc_mux_enum_get, >+ .put = alc_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+/* capture mixer elements (in case NID 0x07 not available) */ >+static struct snd_kcontrol_new alc880_capture_alt_mixer[] = { >+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ /* The multiple "Capture Source" controls confuse alsamixer >+ * So call somewhat different.. >+ */ >+ /* .name = "Capture Source", */ >+ .name = "Input Source", >+ .count = 2, >+ .info = alc_mux_enum_info, >+ .get = alc_mux_enum_get, >+ .put = alc_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+ >+ >+/* >+ * ALC880 5-stack model >+ * >+ * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0d), >+ * Side = 0x02 (0xd) >+ * Pin assignment: Front = 0x14, Surr = 0x17, CLFE = 0x16 >+ * Line-In/Side = 0x1a, Mic = 0x18, F-Mic = 0x1b, HP = 0x19 >+ */ >+ >+/* additional mixers to alc880_three_stack_mixer */ >+static struct snd_kcontrol_new alc880_five_stack_mixer[] = { >+ HDA_CODEC_VOLUME("Side Playback Volume", 0x0d, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Side Playback Switch", 0x0d, 2, HDA_INPUT), >+ { } /* end */ >+}; >+ >+/* channel source setting (6/8 channel selection for 5-stack) */ >+/* 6ch mode */ >+static struct hda_verb alc880_fivestack_ch6_init[] = { >+ /* set line-in to input, mute it */ >+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, >+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, >+ { } /* end */ >+}; >+ >+/* 8ch mode */ >+static struct hda_verb alc880_fivestack_ch8_init[] = { >+ /* set line-in to output, unmute it */ >+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, >+ { } /* end */ >+}; >+ >+static struct hda_channel_mode alc880_fivestack_modes[2] = { >+ { 6, alc880_fivestack_ch6_init }, >+ { 8, alc880_fivestack_ch8_init }, >+}; >+ >+ >+/* >+ * ALC880 6-stack model >+ * >+ * DAC: Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e), >+ * Side = 0x05 (0x0f) >+ * Pin assignment: Front = 0x14, Surr = 0x15, CLFE = 0x16, Side = 0x17, >+ * Mic = 0x18, F-Mic = 0x19, Line = 0x1a, HP = 0x1b >+ */ >+ >+static hda_nid_t alc880_6st_dac_nids[4] = { >+ /* front, rear, clfe, rear_surr */ >+ 0x02, 0x03, 0x04, 0x05 >+}; >+ >+static struct hda_input_mux alc880_6stack_capture_source = { >+ .num_items = 4, >+ .items = { >+ { "Mic", 0x0 }, >+ { "Front Mic", 0x1 }, >+ { "Line", 0x2 }, >+ { "CD", 0x4 }, >+ }, >+}; >+ >+/* fixed 8-channels */ >+static struct hda_channel_mode alc880_sixstack_modes[1] = { >+ { 8, NULL }, >+}; >+ >+static struct snd_kcontrol_new alc880_six_stack_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), >+ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), >+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Channel Mode", >+ .info = alc_ch_mode_info, >+ .get = alc_ch_mode_get, >+ .put = alc_ch_mode_put, >+ }, >+ { } /* end */ >+}; >+ >+ >+/* >+ * ALC880 W810 model >+ * >+ * W810 has rear IO for: >+ * Front (DAC 02) >+ * Surround (DAC 03) >+ * Center/LFE (DAC 04) >+ * Digital out (06) >+ * >+ * The system also has a pair of internal speakers, and a headphone jack. >+ * These are both connected to Line2 on the codec, hence to DAC 02. >+ * >+ * There is a variable resistor to control the speaker or headphone >+ * volume. This is a hardware-only device without a software API. >+ * >+ * Plugging headphones in will disable the internal speakers. This is >+ * implemented in hardware, not via the driver using jack sense. In >+ * a similar fashion, plugging into the rear socket marked "front" will >+ * disable both the speakers and headphones. >+ * >+ * For input, there's a microphone jack, and an "audio in" jack. >+ * These may not do anything useful with this driver yet, because I >+ * haven't setup any initialization verbs for these yet... >+ */ >+ >+static hda_nid_t alc880_w810_dac_nids[3] = { >+ /* front, rear/surround, clfe */ >+ 0x02, 0x03, 0x04 >+}; >+ >+/* fixed 6 channels */ >+static struct hda_channel_mode alc880_w810_modes[1] = { >+ { 6, NULL } >+}; >+ >+/* Pin assignment: Front = 0x14, Surr = 0x15, CLFE = 0x16, HP = 0x1b */ >+static struct snd_kcontrol_new alc880_w810_base_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), >+ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), >+ { } /* end */ >+}; >+ >+ >+/* >+ * Z710V model >+ * >+ * DAC: Front = 0x02 (0x0c), HP = 0x03 (0x0d) >+ * Pin assignment: Front = 0x14, HP = 0x15, Mic = 0x18, Mic2 = 0x19(?), >+ * Line = 0x1a >+ */ >+ >+static hda_nid_t alc880_z71v_dac_nids[1] = { >+ 0x02 >+}; >+#define ALC880_Z71V_HP_DAC 0x03 >+ >+/* fixed 2 channels */ >+static struct hda_channel_mode alc880_2_jack_modes[1] = { >+ { 2, NULL } >+}; >+ >+static struct snd_kcontrol_new alc880_z71v_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ { } /* end */ >+}; >+ >+ >+/* >+ * ALC880 F1734 model >+ * >+ * DAC: HP = 0x02 (0x0c), Front = 0x03 (0x0d) >+ * Pin assignment: HP = 0x14, Front = 0x15, Mic = 0x18 >+ */ >+ >+static hda_nid_t alc880_f1734_dac_nids[1] = { >+ 0x03 >+}; >+#define ALC880_F1734_HP_DAC 0x02 >+ >+static struct snd_kcontrol_new alc880_f1734_mixer[] = { >+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ { } /* end */ >+}; >+ >+static struct hda_input_mux alc880_f1734_capture_source = { >+ .num_items = 2, >+ .items = { >+ { "Mic", 0x1 }, >+ { "CD", 0x4 }, >+ }, >+}; >+ >+ >+/* >+ * ALC880 ASUS model >+ * >+ * DAC: HP/Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e) >+ * Pin assignment: HP/Front = 0x14, Surr = 0x15, CLFE = 0x16, >+ * Mic = 0x18, Line = 0x1a >+ */ >+ >+#define alc880_asus_dac_nids alc880_w810_dac_nids /* identical with w810 */ >+#define alc880_asus_modes alc880_threestack_modes /* 2/6 channel mode */ >+ >+static struct snd_kcontrol_new alc880_asus_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), >+ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Channel Mode", >+ .info = alc_ch_mode_info, >+ .get = alc_ch_mode_get, >+ .put = alc_ch_mode_put, >+ }, >+ { } /* end */ >+}; >+ >+/* >+ * ALC880 ASUS W1V model >+ * >+ * DAC: HP/Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e) >+ * Pin assignment: HP/Front = 0x14, Surr = 0x15, CLFE = 0x16, >+ * Mic = 0x18, Line = 0x1a, Line2 = 0x1b >+ */ >+ >+/* additional mixers to alc880_asus_mixer */ >+static struct snd_kcontrol_new alc880_asus_w1v_mixer[] = { >+ HDA_CODEC_VOLUME("Line2 Playback Volume", 0x0b, 0x03, HDA_INPUT), >+ HDA_CODEC_MUTE("Line2 Playback Switch", 0x0b, 0x03, HDA_INPUT), >+ { } /* end */ >+}; >+ >+/* additional mixers to alc880_asus_mixer */ >+static struct snd_kcontrol_new alc880_pcbeep_mixer[] = { >+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), >+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), >+ { } /* end */ >+}; >+ >+/* TCL S700 */ >+static struct snd_kcontrol_new alc880_tcl_s700_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0B, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0B, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0B, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0B, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ /* The multiple "Capture Source" controls confuse alsamixer >+ * So call somewhat different.. >+ */ >+ /* .name = "Capture Source", */ >+ .name = "Input Source", >+ .count = 1, >+ .info = alc_mux_enum_info, >+ .get = alc_mux_enum_get, >+ .put = alc_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+/* Uniwill */ >+static struct snd_kcontrol_new alc880_uniwill_mixer[] = { >+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), >+ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), >+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Channel Mode", >+ .info = alc_ch_mode_info, >+ .get = alc_ch_mode_get, >+ .put = alc_ch_mode_put, >+ }, >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc880_fujitsu_mixer[] = { >+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Ext Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Ext Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc880_uniwill_p53_mixer[] = { >+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ { } /* end */ >+}; >+ >+/* >+ * virtual master controls >+ */ >+ >+/* >+ * slave controls for virtual master >+ */ >+static const char *alc_slave_vols[] = { >+ "Front Playback Volume", >+ "Surround Playback Volume", >+ "Center Playback Volume", >+ "LFE Playback Volume", >+ "Side Playback Volume", >+ "Headphone Playback Volume", >+ "Speaker Playback Volume", >+ "Mono Playback Volume", >+ "Line-Out Playback Volume", >+ NULL, >+}; >+ >+static const char *alc_slave_sws[] = { >+ "Front Playback Switch", >+ "Surround Playback Switch", >+ "Center Playback Switch", >+ "LFE Playback Switch", >+ "Side Playback Switch", >+ "Headphone Playback Switch", >+ "Speaker Playback Switch", >+ "Mono Playback Switch", >+ "IEC958 Playback Switch", >+ NULL, >+}; >+ >+/* >+ * build control elements >+ */ >+static int alc_build_controls(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ int err; >+ int i; >+ >+ 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); >+ 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->vmaster_nid, >+ HDA_OUTPUT, vmaster_tlv); >+ err = snd_hda_add_vmaster(codec, "Master Playback Volume", >+ vmaster_tlv, alc_slave_vols); >+ 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, alc_slave_sws); >+ if (err < 0) >+ return err; >+ } >+ >+ return 0; >+} >+ >+ >+/* >+ * initialize the codec volumes, etc >+ */ >+ >+/* >+ * generic initialization of ADC, input mixers and output mixers >+ */ >+static struct hda_verb alc880_volume_init_verbs[] = { >+ /* >+ * Unmute ADC0-2 and set the default input to mic-in >+ */ >+ {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ >+ /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback >+ * mixer widget >+ * Note: PASD motherboards uses the Line In 2 as the input for front >+ * panel mic (mic 2) >+ */ >+ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, >+ >+ /* >+ * Set up output mixers (0x0c - 0x0f) >+ */ >+ /* set vol=0 to output mixers */ >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ /* set up input amps for analog loopback */ >+ /* Amp Indices: DAC = 0, mixer = 1 */ >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ >+ { } >+}; >+ >+/* >+ * 3-stack pin configuration: >+ * front = 0x14, mic/clfe = 0x18, HP = 0x19, line/surr = 0x1a, f-mic = 0x1b >+ */ >+static struct hda_verb alc880_pin_3stack_init_verbs[] = { >+ /* >+ * preset connection lists of input pins >+ * 0 = front, 1 = rear_surr, 2 = CLFE, 3 = surround >+ */ >+ {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */ >+ {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ >+ {0x12, AC_VERB_SET_CONNECT_SEL, 0x03}, /* line/surround */ >+ >+ /* >+ * Set pin mode and muting >+ */ >+ /* set front pin widgets 0x14 for output */ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ /* Mic1 (rear panel) pin widget for input and vref at 80% */ >+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* Mic2 (as headphone out) for HP output */ >+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ /* Line In pin widget for input */ >+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* Line2 (as front mic) pin widget for input and vref at 80% */ >+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* CD pin widget for input */ >+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ >+ { } >+}; >+ >+/* >+ * 5-stack pin configuration: >+ * front = 0x14, surround = 0x17, clfe = 0x16, mic = 0x18, HP = 0x19, >+ * line-in/side = 0x1a, f-mic = 0x1b >+ */ >+static struct hda_verb alc880_pin_5stack_init_verbs[] = { >+ /* >+ * preset connection lists of input pins >+ * 0 = front, 1 = rear_surr, 2 = CLFE, 3 = surround >+ */ >+ {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ >+ {0x12, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/side */ >+ >+ /* >+ * Set pin mode and muting >+ */ >+ /* set pin widgets 0x14-0x17 for output */ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ /* unmute pins for output (no gain on this amp) */ >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ >+ /* Mic1 (rear panel) pin widget for input and vref at 80% */ >+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* Mic2 (as headphone out) for HP output */ >+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ /* Line In pin widget for input */ >+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* Line2 (as front mic) pin widget for input and vref at 80% */ >+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* CD pin widget for input */ >+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ >+ { } >+}; >+ >+/* >+ * W810 pin configuration: >+ * front = 0x14, surround = 0x15, clfe = 0x16, HP = 0x1b >+ */ >+static struct hda_verb alc880_pin_w810_init_verbs[] = { >+ /* hphone/speaker input selector: front DAC */ >+ {0x13, AC_VERB_SET_CONNECT_SEL, 0x0}, >+ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ >+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ >+ { } >+}; >+ >+/* >+ * Z71V pin configuration: >+ * Speaker-out = 0x14, HP = 0x15, Mic = 0x18, Line-in = 0x1a, Mic2 = 0x1b (?) >+ */ >+static struct hda_verb alc880_pin_z71v_init_verbs[] = { >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ >+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ >+ { } >+}; >+ >+/* >+ * 6-stack pin configuration: >+ * front = 0x14, surr = 0x15, clfe = 0x16, side = 0x17, mic = 0x18, >+ * f-mic = 0x19, line = 0x1a, HP = 0x1b >+ */ >+static struct hda_verb alc880_pin_6stack_init_verbs[] = { >+ {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ >+ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ >+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ >+ { } >+}; >+ >+/* >+ * Uniwill pin configuration: >+ * HP = 0x14, InternalSpeaker = 0x15, mic = 0x18, internal mic = 0x19, >+ * line = 0x1a >+ */ >+static struct hda_verb alc880_uniwill_init_verbs[] = { >+ {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ >+ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, >+ >+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, */ >+ /* {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, */ >+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ >+ {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, >+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, >+ >+ { } >+}; >+ >+/* >+* Uniwill P53 >+* HP = 0x14, InternalSpeaker = 0x15, mic = 0x19, >+ */ >+static struct hda_verb alc880_uniwill_p53_init_verbs[] = { >+ {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ >+ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, >+ >+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ >+ {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, >+ {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_DCVOL_EVENT}, >+ >+ { } >+}; >+ >+static struct hda_verb alc880_beep_init_verbs[] = { >+ { 0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5) }, >+ { } >+}; >+ >+/* toggle speaker-output according to the hp-jack state */ >+static void alc880_uniwill_hp_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ unsigned char bits; >+ >+ present = snd_hda_codec_read(codec, 0x14, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ bits = present ? HDA_AMP_MUTE : 0; >+ snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, bits); >+ snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, bits); >+} >+ >+/* auto-toggle front mic */ >+static void alc880_uniwill_mic_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ unsigned char bits; >+ >+ present = snd_hda_codec_read(codec, 0x18, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ bits = present ? HDA_AMP_MUTE : 0; >+ snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits); >+} >+ >+static void alc880_uniwill_automute(struct hda_codec *codec) >+{ >+ alc880_uniwill_hp_automute(codec); >+ alc880_uniwill_mic_automute(codec); >+} >+ >+static void alc880_uniwill_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ /* Looks like the unsol event is incompatible with the standard >+ * definition. 4bit tag is placed at 28 bit! >+ */ >+ switch (res >> 28) { >+ case ALC880_HP_EVENT: >+ alc880_uniwill_hp_automute(codec); >+ break; >+ case ALC880_MIC_EVENT: >+ alc880_uniwill_mic_automute(codec); >+ break; >+ } >+} >+ >+static void alc880_uniwill_p53_hp_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ unsigned char bits; >+ >+ present = snd_hda_codec_read(codec, 0x14, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ bits = present ? HDA_AMP_MUTE : 0; >+ snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, HDA_AMP_MUTE, bits); >+} >+ >+static void alc880_uniwill_p53_dcvol_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ >+ present = snd_hda_codec_read(codec, 0x21, 0, >+ AC_VERB_GET_VOLUME_KNOB_CONTROL, 0); >+ present &= HDA_AMP_VOLMASK; >+ snd_hda_codec_amp_stereo(codec, 0x0c, HDA_OUTPUT, 0, >+ HDA_AMP_VOLMASK, present); >+ snd_hda_codec_amp_stereo(codec, 0x0d, HDA_OUTPUT, 0, >+ HDA_AMP_VOLMASK, present); >+} >+ >+static void alc880_uniwill_p53_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ /* Looks like the unsol event is incompatible with the standard >+ * definition. 4bit tag is placed at 28 bit! >+ */ >+ if ((res >> 28) == ALC880_HP_EVENT) >+ alc880_uniwill_p53_hp_automute(codec); >+ if ((res >> 28) == ALC880_DCVOL_EVENT) >+ alc880_uniwill_p53_dcvol_automute(codec); >+} >+ >+/* >+ * F1734 pin configuration: >+ * HP = 0x14, speaker-out = 0x15, mic = 0x18 >+ */ >+static struct hda_verb alc880_pin_f1734_init_verbs[] = { >+ {0x07, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, >+ {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x12, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ >+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ >+ {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_HP_EVENT}, >+ {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_DCVOL_EVENT}, >+ >+ { } >+}; >+ >+/* >+ * ASUS pin configuration: >+ * HP/front = 0x14, surr = 0x15, clfe = 0x16, mic = 0x18, line = 0x1a >+ */ >+static struct hda_verb alc880_pin_asus_init_verbs[] = { >+ {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, >+ {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x12, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ >+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ >+ { } >+}; >+ >+/* Enable GPIO mask and set output */ >+#define alc880_gpio1_init_verbs alc_gpio1_init_verbs >+#define alc880_gpio2_init_verbs alc_gpio2_init_verbs >+ >+/* Clevo m520g init */ >+static struct hda_verb alc880_pin_clevo_init_verbs[] = { >+ /* headphone output */ >+ {0x11, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ /* line-out */ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ /* Line-in */ >+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ /* CD */ >+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ /* Mic1 (rear panel) */ >+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ /* Mic2 (front panel) */ >+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ /* headphone */ >+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ /* change to EAPD mode */ >+ {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, >+ {0x20, AC_VERB_SET_PROC_COEF, 0x3060}, >+ >+ { } >+}; >+ >+static struct hda_verb alc880_pin_tcl_S700_init_verbs[] = { >+ /* change to EAPD mode */ >+ {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, >+ {0x20, AC_VERB_SET_PROC_COEF, 0x3060}, >+ >+ /* Headphone output */ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ /* Front output*/ >+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ >+ /* Line In pin widget for input */ >+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ /* CD pin widget for input */ >+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ /* Mic1 (rear panel) pin widget for input and vref at 80% */ >+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ >+ /* change to EAPD mode */ >+ {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, >+ {0x20, AC_VERB_SET_PROC_COEF, 0x3070}, >+ >+ { } >+}; >+ >+/* >+ * LG m1 express dual >+ * >+ * Pin assignment: >+ * Rear Line-In/Out (blue): 0x14 >+ * Build-in Mic-In: 0x15 >+ * Speaker-out: 0x17 >+ * HP-Out (green): 0x1b >+ * Mic-In/Out (red): 0x19 >+ * SPDIF-Out: 0x1e >+ */ >+ >+/* To make 5.1 output working (green=Front, blue=Surr, red=CLFE) */ >+static hda_nid_t alc880_lg_dac_nids[3] = { >+ 0x05, 0x02, 0x03 >+}; >+ >+/* seems analog CD is not working */ >+static struct hda_input_mux alc880_lg_capture_source = { >+ .num_items = 3, >+ .items = { >+ { "Mic", 0x1 }, >+ { "Line", 0x5 }, >+ { "Internal Mic", 0x6 }, >+ }, >+}; >+ >+/* 2,4,6 channel modes */ >+static struct hda_verb alc880_lg_ch2_init[] = { >+ /* set line-in and mic-in to input */ >+ { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, >+ { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, >+ { } >+}; >+ >+static struct hda_verb alc880_lg_ch4_init[] = { >+ /* set line-in to out and mic-in to input */ >+ { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, >+ { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, >+ { } >+}; >+ >+static struct hda_verb alc880_lg_ch6_init[] = { >+ /* set line-in and mic-in to output */ >+ { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, >+ { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, >+ { } >+}; >+ >+static struct hda_channel_mode alc880_lg_ch_modes[3] = { >+ { 2, alc880_lg_ch2_init }, >+ { 4, alc880_lg_ch4_init }, >+ { 6, alc880_lg_ch6_init }, >+}; >+ >+static struct snd_kcontrol_new alc880_lg_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0f, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Front Playback Switch", 0x0f, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Surround Playback Switch", 0x0c, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT), >+ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x06, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x06, HDA_INPUT), >+ HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x07, HDA_INPUT), >+ HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x07, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Channel Mode", >+ .info = alc_ch_mode_info, >+ .get = alc_ch_mode_get, >+ .put = alc_ch_mode_put, >+ }, >+ { } /* end */ >+}; >+ >+static struct hda_verb alc880_lg_init_verbs[] = { >+ /* set capture source to mic-in */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ /* mute all amp mixer inputs */ >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, >+ /* line-in to input */ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ /* built-in mic */ >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ /* speaker-out */ >+ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ /* mic-in to input */ >+ {0x11, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ /* HP-out */ >+ {0x13, AC_VERB_SET_CONNECT_SEL, 0x03}, >+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ /* jack sense */ >+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | 0x1}, >+ { } >+}; >+ >+/* toggle speaker-output according to the hp-jack state */ >+static void alc880_lg_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ unsigned char bits; >+ >+ present = snd_hda_codec_read(codec, 0x1b, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ bits = present ? HDA_AMP_MUTE : 0; >+ snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, bits); >+} >+ >+static void alc880_lg_unsol_event(struct hda_codec *codec, unsigned int res) >+{ >+ /* Looks like the unsol event is incompatible with the standard >+ * definition. 4bit tag is placed at 28 bit! >+ */ >+ if ((res >> 28) == 0x01) >+ alc880_lg_automute(codec); >+} >+ >+/* >+ * LG LW20 >+ * >+ * Pin assignment: >+ * Speaker-out: 0x14 >+ * Mic-In: 0x18 >+ * Built-in Mic-In: 0x19 >+ * Line-In: 0x1b >+ * HP-Out: 0x1a >+ * SPDIF-Out: 0x1e >+ */ >+ >+static struct hda_input_mux alc880_lg_lw_capture_source = { >+ .num_items = 3, >+ .items = { >+ { "Mic", 0x0 }, >+ { "Internal Mic", 0x1 }, >+ { "Line In", 0x2 }, >+ }, >+}; >+ >+#define alc880_lg_lw_modes alc880_threestack_modes >+ >+static struct snd_kcontrol_new alc880_lg_lw_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x0f, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Surround Playback Switch", 0x0f, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), >+ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), >+ HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Channel Mode", >+ .info = alc_ch_mode_info, >+ .get = alc_ch_mode_get, >+ .put = alc_ch_mode_put, >+ }, >+ { } /* end */ >+}; >+ >+static struct hda_verb alc880_lg_lw_init_verbs[] = { >+ {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ >+ {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */ >+ {0x12, AC_VERB_SET_CONNECT_SEL, 0x03}, /* line/surround */ >+ >+ /* set capture source to mic-in */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, >+ /* speaker-out */ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ /* HP-out */ >+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ /* mic-in to input */ >+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ /* built-in mic */ >+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ /* jack sense */ >+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | 0x1}, >+ { } >+}; >+ >+/* toggle speaker-output according to the hp-jack state */ >+static void alc880_lg_lw_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ unsigned char bits; >+ >+ present = snd_hda_codec_read(codec, 0x1b, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ bits = present ? HDA_AMP_MUTE : 0; >+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, bits); >+} >+ >+static void alc880_lg_lw_unsol_event(struct hda_codec *codec, unsigned int res) >+{ >+ /* Looks like the unsol event is incompatible with the standard >+ * definition. 4bit tag is placed at 28 bit! >+ */ >+ if ((res >> 28) == 0x01) >+ alc880_lg_lw_automute(codec); >+} >+ >+static struct snd_kcontrol_new alc880_medion_rim_mixer[] = { >+ HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_MUTE("Internal Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ { } /* end */ >+}; >+ >+static struct hda_input_mux alc880_medion_rim_capture_source = { >+ .num_items = 2, >+ .items = { >+ { "Mic", 0x0 }, >+ { "Internal Mic", 0x1 }, >+ }, >+}; >+ >+static struct hda_verb alc880_medion_rim_init_verbs[] = { >+ {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ >+ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ >+ /* Mic1 (rear panel) pin widget for input and vref at 80% */ >+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* Mic2 (as headphone out) for HP output */ >+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* Internal Speaker */ >+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ >+ {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, >+ {0x20, AC_VERB_SET_PROC_COEF, 0x3060}, >+ >+ {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, >+ { } >+}; >+ >+/* toggle speaker-output according to the hp-jack state */ >+static void alc880_medion_rim_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ unsigned char bits; >+ >+ present = snd_hda_codec_read(codec, 0x14, 0, >+ AC_VERB_GET_PIN_SENSE, 0) >+ & AC_PINSENSE_PRESENCE; >+ bits = present ? HDA_AMP_MUTE : 0; >+ snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, bits); >+ if (present) >+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0); >+ else >+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 2); >+} >+ >+static void alc880_medion_rim_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ /* Looks like the unsol event is incompatible with the standard >+ * definition. 4bit tag is placed at 28 bit! >+ */ >+ if ((res >> 28) == ALC880_HP_EVENT) >+ alc880_medion_rim_automute(codec); >+} >+ >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+static struct hda_amp_list alc880_loopbacks[] = { >+ { 0x0b, HDA_INPUT, 0 }, >+ { 0x0b, HDA_INPUT, 1 }, >+ { 0x0b, HDA_INPUT, 2 }, >+ { 0x0b, HDA_INPUT, 3 }, >+ { 0x0b, HDA_INPUT, 4 }, >+ { } /* end */ >+}; >+ >+static struct hda_amp_list alc880_lg_loopbacks[] = { >+ { 0x0b, HDA_INPUT, 1 }, >+ { 0x0b, HDA_INPUT, 6 }, >+ { 0x0b, HDA_INPUT, 7 }, >+ { } /* end */ >+}; >+#endif >+ >+/* >+ * Common callbacks >+ */ >+ >+static int alc_init(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ unsigned int i; >+ >+ alc_fix_pll(codec); >+ >+ for (i = 0; i < spec->num_init_verbs; i++) >+ snd_hda_sequence_write(codec, spec->init_verbs[i]); >+ >+ if (spec->init_hook) >+ spec->init_hook(codec); >+ >+ 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 alc880_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 alc880_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 alc880_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 alc880_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 alc880_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 alc880_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 alc880_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 alc880_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; >+} >+ >+ >+/* >+ */ >+static struct hda_pcm_stream alc880_pcm_analog_playback = { >+ .substreams = 1, >+ .channels_min = 2, >+ .channels_max = 8, >+ /* NID is set in alc_build_pcms */ >+ .ops = { >+ .open = alc880_playback_pcm_open, >+ .prepare = alc880_playback_pcm_prepare, >+ .cleanup = alc880_playback_pcm_cleanup >+ }, >+}; >+ >+static struct hda_pcm_stream alc880_pcm_analog_capture = { >+ .substreams = 1, >+ .channels_min = 2, >+ .channels_max = 2, >+ /* NID is set in alc_build_pcms */ >+}; >+ >+static struct hda_pcm_stream alc880_pcm_analog_alt_playback = { >+ .substreams = 1, >+ .channels_min = 2, >+ .channels_max = 2, >+ /* NID is set in alc_build_pcms */ >+}; >+ >+static struct hda_pcm_stream alc880_pcm_analog_alt_capture = { >+ .substreams = 2, /* can be overridden */ >+ .channels_min = 2, >+ .channels_max = 2, >+ /* NID is set in alc_build_pcms */ >+ .ops = { >+ .prepare = alc880_alt_capture_pcm_prepare, >+ .cleanup = alc880_alt_capture_pcm_cleanup >+ }, >+}; >+ >+static struct hda_pcm_stream alc880_pcm_digital_playback = { >+ .substreams = 1, >+ .channels_min = 2, >+ .channels_max = 2, >+ /* NID is set in alc_build_pcms */ >+ .ops = { >+ .open = alc880_dig_playback_pcm_open, >+ .close = alc880_dig_playback_pcm_close, >+ .prepare = alc880_dig_playback_pcm_prepare >+ }, >+}; >+ >+static struct hda_pcm_stream alc880_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 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; >+ int i; >+ >+ codec->num_pcms = 1; >+ codec->pcm_info = info; >+ >+ info->name = spec->stream_name_analog; >+ if (spec->stream_analog_playback) { >+ snd_assert(spec->multiout.dac_nids, return -EINVAL); >+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback); >+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; >+ } >+ if (spec->stream_analog_capture) { >+ snd_assert(spec->adc_nids, return -EINVAL); >+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture); >+ 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; >+ } >+ } >+ } >+ >+ /* SPDIF for stream index #1 */ >+ if (spec->multiout.dig_out_nid || spec->dig_in_nid) { >+ codec->num_pcms = 2; >+ info = spec->pcm_rec + 1; >+ info->name = spec->stream_name_digital; >+ info->pcm_type = HDA_PCM_TYPE_SPDIF; >+ if (spec->multiout.dig_out_nid && >+ spec->stream_digital_playback) { >+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_digital_playback); >+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; >+ } >+ if (spec->dig_in_nid && >+ spec->stream_digital_capture) { >+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_digital_capture); >+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; >+ } >+ } >+ >+ /* If the use of more than one ADC is requested for the current >+ * model, configure a second analog capture-only PCM. >+ */ >+ /* Additional Analaog capture for index #2 */ >+ if ((spec->alt_dac_nid && spec->stream_analog_alt_playback) || >+ (spec->num_adc_nids > 1 && spec->stream_analog_alt_capture)) { >+ codec->num_pcms = 3; >+ info = spec->pcm_rec + 2; >+ info->name = spec->stream_name_analog; >+ if (spec->alt_dac_nid) { >+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = >+ *spec->stream_analog_alt_playback; >+ 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 (spec->num_adc_nids > 1) { >+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = >+ *spec->stream_analog_alt_capture; >+ 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 void alc_free(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ unsigned int i; >+ >+ if (!spec) >+ return; >+ >+ if (spec->kctl_alloc) { >+ for (i = 0; i < spec->num_kctl_used; i++) >+ kfree(spec->kctl_alloc[i].name); >+ kfree(spec->kctl_alloc); >+ } >+ kfree(spec); >+ codec->spec = NULL; /* to be sure */ >+} >+ >+/* >+ */ >+static 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_SND_HDA_POWER_SAVE >+ .check_power_status = alc_check_power_status, >+#endif >+}; >+ >+ >+/* >+ * Test configuration for debugging >+ * >+ * Almost all inputs/outputs are enabled. I/O pins can be configured via >+ * enum controls. >+ */ >+#ifdef CONFIG_SND_DEBUG >+static hda_nid_t alc880_test_dac_nids[4] = { >+ 0x02, 0x03, 0x04, 0x05 >+}; >+ >+static struct hda_input_mux alc880_test_capture_source = { >+ .num_items = 7, >+ .items = { >+ { "In-1", 0x0 }, >+ { "In-2", 0x1 }, >+ { "In-3", 0x2 }, >+ { "In-4", 0x3 }, >+ { "CD", 0x4 }, >+ { "Front", 0x5 }, >+ { "Surround", 0x6 }, >+ }, >+}; >+ >+static struct hda_channel_mode alc880_test_modes[4] = { >+ { 2, NULL }, >+ { 4, NULL }, >+ { 6, NULL }, >+ { 8, NULL }, >+}; >+ >+static int alc_test_pin_ctl_info(struct snd_kcontrol *kcontrol, >+ struct snd_ctl_elem_info *uinfo) >+{ >+ static char *texts[] = { >+ "N/A", "Line Out", "HP Out", >+ "In Hi-Z", "In 50%", "In Grd", "In 80%", "In 100%" >+ }; >+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; >+ uinfo->count = 1; >+ uinfo->value.enumerated.items = 8; >+ if (uinfo->value.enumerated.item >= 8) >+ uinfo->value.enumerated.item = 7; >+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); >+ return 0; >+} >+ >+static int alc_test_pin_ctl_get(struct snd_kcontrol *kcontrol, >+ struct snd_ctl_elem_value *ucontrol) >+{ >+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); >+ hda_nid_t nid = (hda_nid_t)kcontrol->private_value; >+ unsigned int pin_ctl, item = 0; >+ >+ pin_ctl = snd_hda_codec_read(codec, nid, 0, >+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0); >+ if (pin_ctl & AC_PINCTL_OUT_EN) { >+ if (pin_ctl & AC_PINCTL_HP_EN) >+ item = 2; >+ else >+ item = 1; >+ } else if (pin_ctl & AC_PINCTL_IN_EN) { >+ switch (pin_ctl & AC_PINCTL_VREFEN) { >+ case AC_PINCTL_VREF_HIZ: item = 3; break; >+ case AC_PINCTL_VREF_50: item = 4; break; >+ case AC_PINCTL_VREF_GRD: item = 5; break; >+ case AC_PINCTL_VREF_80: item = 6; break; >+ case AC_PINCTL_VREF_100: item = 7; break; >+ } >+ } >+ ucontrol->value.enumerated.item[0] = item; >+ return 0; >+} >+ >+static int alc_test_pin_ctl_put(struct snd_kcontrol *kcontrol, >+ struct snd_ctl_elem_value *ucontrol) >+{ >+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); >+ hda_nid_t nid = (hda_nid_t)kcontrol->private_value; >+ static unsigned int ctls[] = { >+ 0, AC_PINCTL_OUT_EN, AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN, >+ AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ, >+ AC_PINCTL_IN_EN | AC_PINCTL_VREF_50, >+ AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD, >+ AC_PINCTL_IN_EN | AC_PINCTL_VREF_80, >+ AC_PINCTL_IN_EN | AC_PINCTL_VREF_100, >+ }; >+ unsigned int old_ctl, new_ctl; >+ >+ old_ctl = snd_hda_codec_read(codec, nid, 0, >+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0); >+ new_ctl = ctls[ucontrol->value.enumerated.item[0]]; >+ if (old_ctl != new_ctl) { >+ int val; >+ snd_hda_codec_write_cache(codec, nid, 0, >+ AC_VERB_SET_PIN_WIDGET_CONTROL, >+ new_ctl); >+ val = ucontrol->value.enumerated.item[0] >= 3 ? >+ HDA_AMP_MUTE : 0; >+ snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, val); >+ return 1; >+ } >+ return 0; >+} >+ >+static int alc_test_pin_src_info(struct snd_kcontrol *kcontrol, >+ struct snd_ctl_elem_info *uinfo) >+{ >+ static char *texts[] = { >+ "Front", "Surround", "CLFE", "Side" >+ }; >+ 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 alc_test_pin_src_get(struct snd_kcontrol *kcontrol, >+ struct snd_ctl_elem_value *ucontrol) >+{ >+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); >+ hda_nid_t nid = (hda_nid_t)kcontrol->private_value; >+ unsigned int sel; >+ >+ sel = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0); >+ ucontrol->value.enumerated.item[0] = sel & 3; >+ return 0; >+} >+ >+static int alc_test_pin_src_put(struct snd_kcontrol *kcontrol, >+ struct snd_ctl_elem_value *ucontrol) >+{ >+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); >+ hda_nid_t nid = (hda_nid_t)kcontrol->private_value; >+ unsigned int sel; >+ >+ sel = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0) & 3; >+ if (ucontrol->value.enumerated.item[0] != sel) { >+ sel = ucontrol->value.enumerated.item[0] & 3; >+ snd_hda_codec_write_cache(codec, nid, 0, >+ AC_VERB_SET_CONNECT_SEL, sel); >+ return 1; >+ } >+ return 0; >+} >+ >+#define PIN_CTL_TEST(xname,nid) { \ >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ >+ .name = xname, \ >+ .info = alc_test_pin_ctl_info, \ >+ .get = alc_test_pin_ctl_get, \ >+ .put = alc_test_pin_ctl_put, \ >+ .private_value = nid \ >+ } >+ >+#define PIN_SRC_TEST(xname,nid) { \ >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ >+ .name = xname, \ >+ .info = alc_test_pin_src_info, \ >+ .get = alc_test_pin_src_get, \ >+ .put = alc_test_pin_src_put, \ >+ .private_value = nid \ >+ } >+ >+static struct snd_kcontrol_new alc880_test_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("CLFE Playback Volume", 0x0e, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), >+ HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), >+ HDA_BIND_MUTE("CLFE Playback Switch", 0x0e, 2, HDA_INPUT), >+ HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), >+ PIN_CTL_TEST("Front Pin Mode", 0x14), >+ PIN_CTL_TEST("Surround Pin Mode", 0x15), >+ PIN_CTL_TEST("CLFE Pin Mode", 0x16), >+ PIN_CTL_TEST("Side Pin Mode", 0x17), >+ PIN_CTL_TEST("In-1 Pin Mode", 0x18), >+ PIN_CTL_TEST("In-2 Pin Mode", 0x19), >+ PIN_CTL_TEST("In-3 Pin Mode", 0x1a), >+ PIN_CTL_TEST("In-4 Pin Mode", 0x1b), >+ PIN_SRC_TEST("In-1 Pin Source", 0x18), >+ PIN_SRC_TEST("In-2 Pin Source", 0x19), >+ PIN_SRC_TEST("In-3 Pin Source", 0x1a), >+ PIN_SRC_TEST("In-4 Pin Source", 0x1b), >+ HDA_CODEC_VOLUME("In-1 Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("In-1 Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("In-2 Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_MUTE("In-2 Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("In-3 Playback Volume", 0x0b, 0x2, HDA_INPUT), >+ HDA_CODEC_MUTE("In-3 Playback Switch", 0x0b, 0x2, HDA_INPUT), >+ HDA_CODEC_VOLUME("In-4 Playback Volume", 0x0b, 0x3, HDA_INPUT), >+ HDA_CODEC_MUTE("In-4 Playback Switch", 0x0b, 0x3, HDA_INPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x4, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x4, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Channel Mode", >+ .info = alc_ch_mode_info, >+ .get = alc_ch_mode_get, >+ .put = alc_ch_mode_put, >+ }, >+ { } /* end */ >+}; >+ >+static struct hda_verb alc880_test_init_verbs[] = { >+ /* Unmute inputs of 0x0c - 0x0f */ >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ /* Vol output for 0x0c-0x0f */ >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ /* Set output pins 0x14-0x17 */ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ /* Unmute output pins 0x14-0x17 */ >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ /* Set input pins 0x18-0x1c */ >+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ /* Mute input pins 0x18-0x1b */ >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* ADC set up */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ /* Analog input/passthru */ >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, >+ { } >+}; >+#endif >+ >+/* >+ */ >+ >+static const char *alc880_models[ALC880_MODEL_LAST] = { >+ [ALC880_3ST] = "3stack", >+ [ALC880_TCL_S700] = "tcl", >+ [ALC880_3ST_DIG] = "3stack-digout", >+ [ALC880_CLEVO] = "clevo", >+ [ALC880_5ST] = "5stack", >+ [ALC880_5ST_DIG] = "5stack-digout", >+ [ALC880_W810] = "w810", >+ [ALC880_Z71V] = "z71v", >+ [ALC880_6ST] = "6stack", >+ [ALC880_6ST_DIG] = "6stack-digout", >+ [ALC880_ASUS] = "asus", >+ [ALC880_ASUS_W1V] = "asus-w1v", >+ [ALC880_ASUS_DIG] = "asus-dig", >+ [ALC880_ASUS_DIG2] = "asus-dig2", >+ [ALC880_UNIWILL_DIG] = "uniwill", >+ [ALC880_UNIWILL_P53] = "uniwill-p53", >+ [ALC880_FUJITSU] = "fujitsu", >+ [ALC880_F1734] = "F1734", >+ [ALC880_LG] = "lg", >+ [ALC880_LG_LW] = "lg-lw", >+ [ALC880_MEDION_RIM] = "medion", >+#ifdef CONFIG_SND_DEBUG >+ [ALC880_TEST] = "test", >+#endif >+ [ALC880_AUTO] = "auto", >+}; >+ >+static struct snd_pci_quirk alc880_cfg_tbl[] = { >+ SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_W810), >+ SND_PCI_QUIRK(0x1019, 0xa880, "ECS", ALC880_5ST_DIG), >+ SND_PCI_QUIRK(0x1019, 0xa884, "Acer APFV", ALC880_6ST), >+ SND_PCI_QUIRK(0x1025, 0x0070, "ULI", ALC880_3ST_DIG), >+ SND_PCI_QUIRK(0x1025, 0x0077, "ULI", ALC880_6ST_DIG), >+ SND_PCI_QUIRK(0x1025, 0x0078, "ULI", ALC880_6ST_DIG), >+ SND_PCI_QUIRK(0x1025, 0x0087, "ULI", ALC880_6ST_DIG), >+ SND_PCI_QUIRK(0x1025, 0xe309, "ULI", ALC880_3ST_DIG), >+ SND_PCI_QUIRK(0x1025, 0xe310, "ULI", ALC880_3ST), >+ SND_PCI_QUIRK(0x1039, 0x1234, NULL, ALC880_6ST_DIG), >+ SND_PCI_QUIRK(0x103c, 0x2a09, "HP", ALC880_5ST), >+ SND_PCI_QUIRK(0x1043, 0x10b3, "ASUS W1V", ALC880_ASUS_W1V), >+ SND_PCI_QUIRK(0x1043, 0x10c2, "ASUS W6A", ALC880_ASUS_DIG), >+ SND_PCI_QUIRK(0x1043, 0x10c3, "ASUS Wxx", ALC880_ASUS_DIG), >+ SND_PCI_QUIRK(0x1043, 0x1113, "ASUS", ALC880_ASUS_DIG), >+ SND_PCI_QUIRK(0x1043, 0x1123, "ASUS", ALC880_ASUS_DIG), >+ SND_PCI_QUIRK(0x1043, 0x1173, "ASUS", ALC880_ASUS_DIG), >+ SND_PCI_QUIRK(0x1043, 0x1964, "ASUS Z71V", ALC880_Z71V), >+ /* SND_PCI_QUIRK(0x1043, 0x1964, "ASUS", ALC880_ASUS_DIG), */ >+ SND_PCI_QUIRK(0x1043, 0x1973, "ASUS", ALC880_ASUS_DIG), >+ SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS", ALC880_ASUS_DIG), >+ SND_PCI_QUIRK(0x1043, 0x814e, "ASUS P5GD1 w/SPDIF", ALC880_6ST_DIG), >+ SND_PCI_QUIRK(0x1043, 0x8181, "ASUS P4GPL", ALC880_ASUS_DIG), >+ SND_PCI_QUIRK(0x1043, 0x8196, "ASUS P5GD1", ALC880_6ST), >+ SND_PCI_QUIRK(0x1043, 0x81b4, "ASUS", ALC880_6ST), >+ SND_PCI_QUIRK(0x1043, 0, "ASUS", ALC880_ASUS), /* default ASUS */ >+ SND_PCI_QUIRK(0x104d, 0x81a0, "Sony", ALC880_3ST), >+ SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_3ST), >+ SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_5ST), >+ SND_PCI_QUIRK(0x107b, 0x3033, "Gateway", ALC880_5ST), >+ SND_PCI_QUIRK(0x107b, 0x4039, "Gateway", ALC880_5ST), >+ SND_PCI_QUIRK(0x1297, 0xc790, "Shuttle ST20G5", ALC880_6ST_DIG), >+ SND_PCI_QUIRK(0x1458, 0xa102, "Gigabyte K8", ALC880_6ST_DIG), >+ SND_PCI_QUIRK(0x1462, 0x1150, "MSI", ALC880_6ST_DIG), >+ SND_PCI_QUIRK(0x1509, 0x925d, "FIC P4M", ALC880_6ST_DIG), >+ SND_PCI_QUIRK(0x1558, 0x0520, "Clevo m520G", ALC880_CLEVO), >+ SND_PCI_QUIRK(0x1558, 0x0660, "Clevo m655n", ALC880_CLEVO), >+ SND_PCI_QUIRK(0x1558, 0x5401, "ASUS", ALC880_ASUS_DIG2), >+ SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_5ST_DIG), >+ SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_UNIWILL_DIG), >+ SND_PCI_QUIRK(0x1584, 0x9054, "Uniwlll", ALC880_F1734), >+ SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_UNIWILL), >+ SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_UNIWILL_P53), >+ SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_W810), >+ SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_MEDION_RIM), >+ SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_5ST_DIG), >+ SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_5ST_DIG), >+ SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_F1734), >+ SND_PCI_QUIRK(0x1734, 0x10ac, "FSC", ALC880_UNIWILL), >+ SND_PCI_QUIRK(0x1734, 0x10b0, "Fujitsu", ALC880_FUJITSU), >+ SND_PCI_QUIRK(0x1854, 0x0018, "LG LW20", ALC880_LG_LW), >+ SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_LG), >+ SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_LG), >+ SND_PCI_QUIRK(0x1854, 0x0077, "LG LW25", ALC880_LG_LW), >+ SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_TCL_S700), >+ SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_6ST_DIG), /* broken BIOS */ >+ SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_6ST_DIG), >+ SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_5ST_DIG), >+ SND_PCI_QUIRK(0x8086, 0xd400, "Intel mobo", ALC880_5ST_DIG), >+ SND_PCI_QUIRK(0x8086, 0xd401, "Intel mobo", ALC880_5ST_DIG), >+ SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_3ST_DIG), >+ SND_PCI_QUIRK(0x8086, 0xe224, "Intel mobo", ALC880_5ST_DIG), >+ SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_3ST_DIG), >+ SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_3ST_DIG), >+ SND_PCI_QUIRK(0x8086, 0xe400, "Intel mobo", ALC880_5ST_DIG), >+ SND_PCI_QUIRK(0x8086, 0xe401, "Intel mobo", ALC880_5ST_DIG), >+ SND_PCI_QUIRK(0x8086, 0xe402, "Intel mobo", ALC880_5ST_DIG), >+ SND_PCI_QUIRK(0x8086, 0, "Intel mobo", ALC880_3ST), /* default Intel */ >+ SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_5ST_DIG), >+ SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_6ST_DIG), >+ {} >+}; >+ >+/* >+ * ALC880 codec presets >+ */ >+static struct alc_config_preset alc880_presets[] = { >+ [ALC880_3ST] = { >+ .mixers = { alc880_three_stack_mixer }, >+ .init_verbs = { alc880_volume_init_verbs, >+ alc880_pin_3stack_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc880_dac_nids), >+ .dac_nids = alc880_dac_nids, >+ .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), >+ .channel_mode = alc880_threestack_modes, >+ .need_dac_fix = 1, >+ .input_mux = &alc880_capture_source, >+ }, >+ [ALC880_3ST_DIG] = { >+ .mixers = { alc880_three_stack_mixer }, >+ .init_verbs = { alc880_volume_init_verbs, >+ alc880_pin_3stack_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc880_dac_nids), >+ .dac_nids = alc880_dac_nids, >+ .dig_out_nid = ALC880_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), >+ .channel_mode = alc880_threestack_modes, >+ .need_dac_fix = 1, >+ .input_mux = &alc880_capture_source, >+ }, >+ [ALC880_TCL_S700] = { >+ .mixers = { alc880_tcl_s700_mixer }, >+ .init_verbs = { alc880_volume_init_verbs, >+ alc880_pin_tcl_S700_init_verbs, >+ alc880_gpio2_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc880_dac_nids), >+ .dac_nids = alc880_dac_nids, >+ .hp_nid = 0x03, >+ .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes), >+ .channel_mode = alc880_2_jack_modes, >+ .input_mux = &alc880_capture_source, >+ }, >+ [ALC880_5ST] = { >+ .mixers = { alc880_three_stack_mixer, >+ alc880_five_stack_mixer}, >+ .init_verbs = { alc880_volume_init_verbs, >+ alc880_pin_5stack_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc880_dac_nids), >+ .dac_nids = alc880_dac_nids, >+ .num_channel_mode = ARRAY_SIZE(alc880_fivestack_modes), >+ .channel_mode = alc880_fivestack_modes, >+ .input_mux = &alc880_capture_source, >+ }, >+ [ALC880_5ST_DIG] = { >+ .mixers = { alc880_three_stack_mixer, >+ alc880_five_stack_mixer }, >+ .init_verbs = { alc880_volume_init_verbs, >+ alc880_pin_5stack_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc880_dac_nids), >+ .dac_nids = alc880_dac_nids, >+ .dig_out_nid = ALC880_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc880_fivestack_modes), >+ .channel_mode = alc880_fivestack_modes, >+ .input_mux = &alc880_capture_source, >+ }, >+ [ALC880_6ST] = { >+ .mixers = { alc880_six_stack_mixer }, >+ .init_verbs = { alc880_volume_init_verbs, >+ alc880_pin_6stack_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc880_6st_dac_nids), >+ .dac_nids = alc880_6st_dac_nids, >+ .num_channel_mode = ARRAY_SIZE(alc880_sixstack_modes), >+ .channel_mode = alc880_sixstack_modes, >+ .input_mux = &alc880_6stack_capture_source, >+ }, >+ [ALC880_6ST_DIG] = { >+ .mixers = { alc880_six_stack_mixer }, >+ .init_verbs = { alc880_volume_init_verbs, >+ alc880_pin_6stack_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc880_6st_dac_nids), >+ .dac_nids = alc880_6st_dac_nids, >+ .dig_out_nid = ALC880_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc880_sixstack_modes), >+ .channel_mode = alc880_sixstack_modes, >+ .input_mux = &alc880_6stack_capture_source, >+ }, >+ [ALC880_W810] = { >+ .mixers = { alc880_w810_base_mixer }, >+ .init_verbs = { alc880_volume_init_verbs, >+ alc880_pin_w810_init_verbs, >+ alc880_gpio2_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc880_w810_dac_nids), >+ .dac_nids = alc880_w810_dac_nids, >+ .dig_out_nid = ALC880_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc880_w810_modes), >+ .channel_mode = alc880_w810_modes, >+ .input_mux = &alc880_capture_source, >+ }, >+ [ALC880_Z71V] = { >+ .mixers = { alc880_z71v_mixer }, >+ .init_verbs = { alc880_volume_init_verbs, >+ alc880_pin_z71v_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc880_z71v_dac_nids), >+ .dac_nids = alc880_z71v_dac_nids, >+ .dig_out_nid = ALC880_DIGOUT_NID, >+ .hp_nid = 0x03, >+ .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes), >+ .channel_mode = alc880_2_jack_modes, >+ .input_mux = &alc880_capture_source, >+ }, >+ [ALC880_F1734] = { >+ .mixers = { alc880_f1734_mixer }, >+ .init_verbs = { alc880_volume_init_verbs, >+ alc880_pin_f1734_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc880_f1734_dac_nids), >+ .dac_nids = alc880_f1734_dac_nids, >+ .hp_nid = 0x02, >+ .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes), >+ .channel_mode = alc880_2_jack_modes, >+ .input_mux = &alc880_f1734_capture_source, >+ .unsol_event = alc880_uniwill_p53_unsol_event, >+ .init_hook = alc880_uniwill_p53_hp_automute, >+ }, >+ [ALC880_ASUS] = { >+ .mixers = { alc880_asus_mixer }, >+ .init_verbs = { alc880_volume_init_verbs, >+ alc880_pin_asus_init_verbs, >+ alc880_gpio1_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), >+ .dac_nids = alc880_asus_dac_nids, >+ .num_channel_mode = ARRAY_SIZE(alc880_asus_modes), >+ .channel_mode = alc880_asus_modes, >+ .need_dac_fix = 1, >+ .input_mux = &alc880_capture_source, >+ }, >+ [ALC880_ASUS_DIG] = { >+ .mixers = { alc880_asus_mixer }, >+ .init_verbs = { alc880_volume_init_verbs, >+ alc880_pin_asus_init_verbs, >+ alc880_gpio1_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), >+ .dac_nids = alc880_asus_dac_nids, >+ .dig_out_nid = ALC880_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc880_asus_modes), >+ .channel_mode = alc880_asus_modes, >+ .need_dac_fix = 1, >+ .input_mux = &alc880_capture_source, >+ }, >+ [ALC880_ASUS_DIG2] = { >+ .mixers = { alc880_asus_mixer }, >+ .init_verbs = { alc880_volume_init_verbs, >+ alc880_pin_asus_init_verbs, >+ alc880_gpio2_init_verbs }, /* use GPIO2 */ >+ .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), >+ .dac_nids = alc880_asus_dac_nids, >+ .dig_out_nid = ALC880_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc880_asus_modes), >+ .channel_mode = alc880_asus_modes, >+ .need_dac_fix = 1, >+ .input_mux = &alc880_capture_source, >+ }, >+ [ALC880_ASUS_W1V] = { >+ .mixers = { alc880_asus_mixer, alc880_asus_w1v_mixer }, >+ .init_verbs = { alc880_volume_init_verbs, >+ alc880_pin_asus_init_verbs, >+ alc880_gpio1_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), >+ .dac_nids = alc880_asus_dac_nids, >+ .dig_out_nid = ALC880_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc880_asus_modes), >+ .channel_mode = alc880_asus_modes, >+ .need_dac_fix = 1, >+ .input_mux = &alc880_capture_source, >+ }, >+ [ALC880_UNIWILL_DIG] = { >+ .mixers = { alc880_asus_mixer, alc880_pcbeep_mixer }, >+ .init_verbs = { alc880_volume_init_verbs, >+ alc880_pin_asus_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), >+ .dac_nids = alc880_asus_dac_nids, >+ .dig_out_nid = ALC880_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc880_asus_modes), >+ .channel_mode = alc880_asus_modes, >+ .need_dac_fix = 1, >+ .input_mux = &alc880_capture_source, >+ }, >+ [ALC880_UNIWILL] = { >+ .mixers = { alc880_uniwill_mixer }, >+ .init_verbs = { alc880_volume_init_verbs, >+ alc880_uniwill_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), >+ .dac_nids = alc880_asus_dac_nids, >+ .dig_out_nid = ALC880_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), >+ .channel_mode = alc880_threestack_modes, >+ .need_dac_fix = 1, >+ .input_mux = &alc880_capture_source, >+ .unsol_event = alc880_uniwill_unsol_event, >+ .init_hook = alc880_uniwill_automute, >+ }, >+ [ALC880_UNIWILL_P53] = { >+ .mixers = { alc880_uniwill_p53_mixer }, >+ .init_verbs = { alc880_volume_init_verbs, >+ alc880_uniwill_p53_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), >+ .dac_nids = alc880_asus_dac_nids, >+ .num_channel_mode = ARRAY_SIZE(alc880_w810_modes), >+ .channel_mode = alc880_threestack_modes, >+ .input_mux = &alc880_capture_source, >+ .unsol_event = alc880_uniwill_p53_unsol_event, >+ .init_hook = alc880_uniwill_p53_hp_automute, >+ }, >+ [ALC880_FUJITSU] = { >+ .mixers = { alc880_fujitsu_mixer, >+ alc880_pcbeep_mixer, }, >+ .init_verbs = { alc880_volume_init_verbs, >+ alc880_uniwill_p53_init_verbs, >+ alc880_beep_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc880_dac_nids), >+ .dac_nids = alc880_dac_nids, >+ .dig_out_nid = ALC880_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes), >+ .channel_mode = alc880_2_jack_modes, >+ .input_mux = &alc880_capture_source, >+ .unsol_event = alc880_uniwill_p53_unsol_event, >+ .init_hook = alc880_uniwill_p53_hp_automute, >+ }, >+ [ALC880_CLEVO] = { >+ .mixers = { alc880_three_stack_mixer }, >+ .init_verbs = { alc880_volume_init_verbs, >+ alc880_pin_clevo_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc880_dac_nids), >+ .dac_nids = alc880_dac_nids, >+ .hp_nid = 0x03, >+ .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), >+ .channel_mode = alc880_threestack_modes, >+ .need_dac_fix = 1, >+ .input_mux = &alc880_capture_source, >+ }, >+ [ALC880_LG] = { >+ .mixers = { alc880_lg_mixer }, >+ .init_verbs = { alc880_volume_init_verbs, >+ alc880_lg_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc880_lg_dac_nids), >+ .dac_nids = alc880_lg_dac_nids, >+ .dig_out_nid = ALC880_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc880_lg_ch_modes), >+ .channel_mode = alc880_lg_ch_modes, >+ .need_dac_fix = 1, >+ .input_mux = &alc880_lg_capture_source, >+ .unsol_event = alc880_lg_unsol_event, >+ .init_hook = alc880_lg_automute, >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+ .loopbacks = alc880_lg_loopbacks, >+#endif >+ }, >+ [ALC880_LG_LW] = { >+ .mixers = { alc880_lg_lw_mixer }, >+ .init_verbs = { alc880_volume_init_verbs, >+ alc880_lg_lw_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc880_dac_nids), >+ .dac_nids = alc880_dac_nids, >+ .dig_out_nid = ALC880_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc880_lg_lw_modes), >+ .channel_mode = alc880_lg_lw_modes, >+ .input_mux = &alc880_lg_lw_capture_source, >+ .unsol_event = alc880_lg_lw_unsol_event, >+ .init_hook = alc880_lg_lw_automute, >+ }, >+ [ALC880_MEDION_RIM] = { >+ .mixers = { alc880_medion_rim_mixer }, >+ .init_verbs = { alc880_volume_init_verbs, >+ alc880_medion_rim_init_verbs, >+ alc_gpio2_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc880_dac_nids), >+ .dac_nids = alc880_dac_nids, >+ .dig_out_nid = ALC880_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes), >+ .channel_mode = alc880_2_jack_modes, >+ .input_mux = &alc880_medion_rim_capture_source, >+ .unsol_event = alc880_medion_rim_unsol_event, >+ .init_hook = alc880_medion_rim_automute, >+ }, >+#ifdef CONFIG_SND_DEBUG >+ [ALC880_TEST] = { >+ .mixers = { alc880_test_mixer }, >+ .init_verbs = { alc880_test_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc880_test_dac_nids), >+ .dac_nids = alc880_test_dac_nids, >+ .dig_out_nid = ALC880_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc880_test_modes), >+ .channel_mode = alc880_test_modes, >+ .input_mux = &alc880_test_capture_source, >+ }, >+#endif >+}; >+ >+/* >+ * Automatic parse of I/O pins from the BIOS configuration >+ */ >+ >+#define NUM_CONTROL_ALLOC 32 >+#define NUM_VERB_ALLOC 32 >+ >+enum { >+ ALC_CTL_WIDGET_VOL, >+ ALC_CTL_WIDGET_MUTE, >+ ALC_CTL_BIND_MUTE, >+}; >+static struct snd_kcontrol_new alc880_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 alc_spec *spec, int type, const char *name, >+ unsigned long val) >+{ >+ struct snd_kcontrol_new *knew; >+ >+ if (spec->num_kctl_used >= spec->num_kctl_alloc) { >+ int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC; >+ >+ /* array + terminator */ >+ knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); >+ if (!knew) >+ return -ENOMEM; >+ if (spec->kctl_alloc) { >+ memcpy(knew, spec->kctl_alloc, >+ sizeof(*knew) * spec->num_kctl_alloc); >+ kfree(spec->kctl_alloc); >+ } >+ spec->kctl_alloc = knew; >+ spec->num_kctl_alloc = num; >+ } >+ >+ knew = &spec->kctl_alloc[spec->num_kctl_used]; >+ *knew = alc880_control_templates[type]; >+ knew->name = kstrdup(name, GFP_KERNEL); >+ if (!knew->name) >+ return -ENOMEM; >+ knew->private_value = val; >+ spec->num_kctl_used++; >+ return 0; >+} >+ >+#define alc880_is_fixed_pin(nid) ((nid) >= 0x14 && (nid) <= 0x17) >+#define alc880_fixed_pin_idx(nid) ((nid) - 0x14) >+#define alc880_is_multi_pin(nid) ((nid) >= 0x18) >+#define alc880_multi_pin_idx(nid) ((nid) - 0x18) >+#define alc880_is_input_pin(nid) ((nid) >= 0x18) >+#define alc880_input_pin_idx(nid) ((nid) - 0x18) >+#define alc880_idx_to_dac(nid) ((nid) + 0x02) >+#define alc880_dac_to_idx(nid) ((nid) - 0x02) >+#define alc880_idx_to_mixer(nid) ((nid) + 0x0c) >+#define alc880_idx_to_selector(nid) ((nid) + 0x10) >+#define ALC880_PIN_CD_NID 0x1c >+ >+/* fill in the dac_nids table from the parsed pin configuration */ >+static int alc880_auto_fill_dac_nids(struct alc_spec *spec, >+ const struct auto_pin_cfg *cfg) >+{ >+ hda_nid_t nid; >+ int assigned[4]; >+ int i, j; >+ >+ memset(assigned, 0, sizeof(assigned)); >+ spec->multiout.dac_nids = spec->private_dac_nids; >+ >+ /* check the pins hardwired to audio widget */ >+ for (i = 0; i < cfg->line_outs; i++) { >+ nid = cfg->line_out_pins[i]; >+ if (alc880_is_fixed_pin(nid)) { >+ int idx = alc880_fixed_pin_idx(nid); >+ spec->multiout.dac_nids[i] = alc880_idx_to_dac(idx); >+ assigned[idx] = 1; >+ } >+ } >+ /* left pins can be connect to any audio widget */ >+ for (i = 0; i < cfg->line_outs; i++) { >+ nid = cfg->line_out_pins[i]; >+ if (alc880_is_fixed_pin(nid)) >+ continue; >+ /* search for an empty channel */ >+ for (j = 0; j < cfg->line_outs; j++) { >+ if (!assigned[j]) { >+ spec->multiout.dac_nids[i] = >+ alc880_idx_to_dac(j); >+ assigned[j] = 1; >+ break; >+ } >+ } >+ } >+ spec->multiout.num_dacs = cfg->line_outs; >+ return 0; >+} >+ >+/* add playback controls from the parsed DAC table */ >+static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec, >+ const struct auto_pin_cfg *cfg) >+{ >+ char name[32]; >+ static const char *chname[4] = { >+ "Front", "Surround", NULL /*CLFE*/, "Side" >+ }; >+ hda_nid_t nid; >+ int i, err; >+ >+ for (i = 0; i < cfg->line_outs; i++) { >+ if (!spec->multiout.dac_nids[i]) >+ continue; >+ nid = alc880_idx_to_mixer(alc880_dac_to_idx(spec->multiout.dac_nids[i])); >+ if (i == 2) { >+ /* Center/LFE */ >+ err = add_control(spec, ALC_CTL_WIDGET_VOL, >+ "Center Playback Volume", >+ HDA_COMPOSE_AMP_VAL(nid, 1, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ err = add_control(spec, ALC_CTL_WIDGET_VOL, >+ "LFE Playback Volume", >+ HDA_COMPOSE_AMP_VAL(nid, 2, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ err = add_control(spec, ALC_CTL_BIND_MUTE, >+ "Center Playback Switch", >+ HDA_COMPOSE_AMP_VAL(nid, 1, 2, >+ HDA_INPUT)); >+ if (err < 0) >+ return err; >+ err = add_control(spec, ALC_CTL_BIND_MUTE, >+ "LFE Playback Switch", >+ 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, ALC_CTL_WIDGET_VOL, name, >+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ sprintf(name, "%s Playback Switch", chname[i]); >+ err = add_control(spec, ALC_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 alc880_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin, >+ const char *pfx) >+{ >+ hda_nid_t nid; >+ int err; >+ char name[32]; >+ >+ if (!pin) >+ return 0; >+ >+ if (alc880_is_fixed_pin(pin)) { >+ nid = alc880_idx_to_dac(alc880_fixed_pin_idx(pin)); >+ /* 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 */ >+ nid = alc880_idx_to_mixer(alc880_fixed_pin_idx(pin)); >+ sprintf(name, "%s Playback Volume", pfx); >+ err = add_control(spec, ALC_CTL_WIDGET_VOL, name, >+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ sprintf(name, "%s Playback Switch", pfx); >+ err = add_control(spec, ALC_CTL_BIND_MUTE, name, >+ HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT)); >+ if (err < 0) >+ return err; >+ } else if (alc880_is_multi_pin(pin)) { >+ /* set manual connection */ >+ /* we have only a switch on HP-out PIN */ >+ sprintf(name, "%s Playback Switch", pfx); >+ err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, >+ HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ } >+ return 0; >+} >+ >+/* 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 idx, hda_nid_t mix_nid) >+{ >+ char name[32]; >+ int err; >+ >+ sprintf(name, "%s Playback Volume", ctlname); >+ err = add_control(spec, ALC_CTL_WIDGET_VOL, name, >+ HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); >+ if (err < 0) >+ return err; >+ sprintf(name, "%s Playback Switch", ctlname); >+ err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, >+ HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); >+ if (err < 0) >+ return err; >+ return 0; >+} >+ >+/* create playback/capture controls for input pins */ >+static int alc880_auto_create_analog_input_ctls(struct alc_spec *spec, >+ const struct auto_pin_cfg *cfg) >+{ >+ struct hda_input_mux *imux = &spec->private_imux; >+ int i, err, idx; >+ >+ for (i = 0; i < AUTO_PIN_LAST; i++) { >+ if (alc880_is_input_pin(cfg->input_pins[i])) { >+ idx = alc880_input_pin_idx(cfg->input_pins[i]); >+ err = new_analog_input(spec, cfg->input_pins[i], >+ auto_pin_cfg_labels[i], >+ idx, 0x0b); >+ if (err < 0) >+ return err; >+ imux->items[imux->num_items].label = >+ auto_pin_cfg_labels[i]; >+ imux->items[imux->num_items].index = >+ alc880_input_pin_idx(cfg->input_pins[i]); >+ imux->num_items++; >+ } >+ } >+ 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 */ >+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, >+ AMP_OUT_UNMUTE); >+} >+ >+static void alc880_auto_set_output_and_unmute(struct hda_codec *codec, >+ hda_nid_t nid, int pin_type, >+ int dac_idx) >+{ >+ alc_set_pin_output(codec, nid, pin_type); >+ /* need the manual connection? */ >+ if (alc880_is_multi_pin(nid)) { >+ struct alc_spec *spec = codec->spec; >+ int idx = alc880_multi_pin_idx(nid); >+ snd_hda_codec_write(codec, alc880_idx_to_selector(idx), 0, >+ AC_VERB_SET_CONNECT_SEL, >+ alc880_dac_to_idx(spec->multiout.dac_nids[dac_idx])); >+ } >+} >+ >+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 alc880_auto_init_multi_out(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ int i; >+ >+ alc_subsystem_id(codec, 0x15, 0x1b, 0x14); >+ for (i = 0; i < spec->autocfg.line_outs; i++) { >+ hda_nid_t nid = spec->autocfg.line_out_pins[i]; >+ int pin_type = get_pin_type(spec->autocfg.line_out_type); >+ alc880_auto_set_output_and_unmute(codec, nid, pin_type, i); >+ } >+} >+ >+static void alc880_auto_init_extra_out(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ hda_nid_t pin; >+ >+ pin = spec->autocfg.speaker_pins[0]; >+ if (pin) /* connect to front */ >+ alc880_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); >+ pin = spec->autocfg.hp_pins[0]; >+ if (pin) /* connect to front */ >+ alc880_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); >+} >+ >+static void alc880_auto_init_analog_input(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ int i; >+ >+ for (i = 0; i < AUTO_PIN_LAST; i++) { >+ hda_nid_t nid = spec->autocfg.input_pins[i]; >+ if (alc880_is_input_pin(nid)) { >+ snd_hda_codec_write(codec, nid, 0, >+ AC_VERB_SET_PIN_WIDGET_CONTROL, >+ i <= AUTO_PIN_FRONT_MIC ? >+ PIN_VREF80 : PIN_IN); >+ if (nid != ALC880_PIN_CD_NID) >+ snd_hda_codec_write(codec, nid, 0, >+ AC_VERB_SET_AMP_GAIN_MUTE, >+ AMP_OUT_MUTE); >+ } >+ } >+} >+ >+/* 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 alc880_parse_auto_config(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ int err; >+ static hda_nid_t alc880_ignore[] = { 0x1d, 0 }; >+ >+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, >+ alc880_ignore); >+ if (err < 0) >+ return err; >+ if (!spec->autocfg.line_outs) >+ return 0; /* can't find valid BIOS pin config */ >+ >+ err = alc880_auto_fill_dac_nids(spec, &spec->autocfg); >+ if (err < 0) >+ return err; >+ err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg); >+ if (err < 0) >+ return err; >+ err = alc880_auto_create_extra_out(spec, >+ spec->autocfg.speaker_pins[0], >+ "Speaker"); >+ if (err < 0) >+ return err; >+ err = alc880_auto_create_extra_out(spec, spec->autocfg.hp_pins[0], >+ "Headphone"); >+ if (err < 0) >+ return err; >+ err = alc880_auto_create_analog_input_ctls(spec, &spec->autocfg); >+ if (err < 0) >+ return err; >+ >+ spec->multiout.max_channels = spec->multiout.num_dacs * 2; >+ >+ if (spec->autocfg.dig_out_pin) >+ spec->multiout.dig_out_nid = ALC880_DIGOUT_NID; >+ if (spec->autocfg.dig_in_pin) >+ spec->dig_in_nid = ALC880_DIGIN_NID; >+ >+ if (spec->kctl_alloc) >+ spec->mixers[spec->num_mixers++] = spec->kctl_alloc; >+ >+ spec->init_verbs[spec->num_init_verbs++] = alc880_volume_init_verbs; >+ >+ spec->num_mux_defs = 1; >+ spec->input_mux = &spec->private_imux; >+ >+ return 1; >+} >+ >+/* additional initialization for auto-configuration model */ >+static void alc880_auto_init(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ alc880_auto_init_multi_out(codec); >+ alc880_auto_init_extra_out(codec); >+ alc880_auto_init_analog_input(codec); >+ if (spec->unsol_event) >+ alc_sku_automute(codec); >+} >+ >+/* >+ * OK, here we have finally the patch for ALC880 >+ */ >+ >+static int patch_alc880(struct hda_codec *codec) >+{ >+ struct alc_spec *spec; >+ int board_config; >+ int err; >+ >+ spec = kzalloc(sizeof(*spec), GFP_KERNEL); >+ if (spec == NULL) >+ return -ENOMEM; >+ >+ codec->spec = spec; >+ >+ board_config = snd_hda_check_board_config(codec, ALC880_MODEL_LAST, >+ alc880_models, >+ alc880_cfg_tbl); >+ if (board_config < 0) { >+ printk(KERN_INFO "hda_codec: Unknown model for ALC880, " >+ "trying auto-probe from BIOS...\n"); >+ board_config = ALC880_AUTO; >+ } >+ >+ if (board_config == ALC880_AUTO) { >+ /* automatic parse from the BIOS config */ >+ err = alc880_parse_auto_config(codec); >+ if (err < 0) { >+ alc_free(codec); >+ return err; >+ } else if (!err) { >+ printk(KERN_INFO >+ "hda_codec: Cannot set up configuration " >+ "from BIOS. Using 3-stack mode...\n"); >+ board_config = ALC880_3ST; >+ } >+ } >+ >+ if (board_config != ALC880_AUTO) >+ setup_preset(spec, &alc880_presets[board_config]); >+ >+ spec->stream_name_analog = "ALC880 Analog"; >+ spec->stream_analog_playback = &alc880_pcm_analog_playback; >+ spec->stream_analog_capture = &alc880_pcm_analog_capture; >+ spec->stream_analog_alt_capture = &alc880_pcm_analog_alt_capture; >+ >+ spec->stream_name_digital = "ALC880 Digital"; >+ spec->stream_digital_playback = &alc880_pcm_digital_playback; >+ spec->stream_digital_capture = &alc880_pcm_digital_capture; >+ >+ if (!spec->adc_nids && spec->input_mux) { >+ /* check whether NID 0x07 is valid */ >+ unsigned int wcap = get_wcaps(codec, alc880_adc_nids[0]); >+ /* get type */ >+ wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; >+ if (wcap != AC_WID_AUD_IN) { >+ spec->adc_nids = alc880_adc_nids_alt; >+ spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids_alt); >+ spec->mixers[spec->num_mixers] = >+ alc880_capture_alt_mixer; >+ spec->num_mixers++; >+ } else { >+ spec->adc_nids = alc880_adc_nids; >+ spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids); >+ spec->mixers[spec->num_mixers] = alc880_capture_mixer; >+ spec->num_mixers++; >+ } >+ } >+ >+ spec->vmaster_nid = 0x0c; >+ >+ codec->patch_ops = alc_patch_ops; >+ if (board_config == ALC880_AUTO) >+ spec->init_hook = alc880_auto_init; >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+ if (!spec->loopback.amplist) >+ spec->loopback.amplist = alc880_loopbacks; >+#endif >+ >+ return 0; >+} >+ >+ >+/* >+ * ALC260 support >+ */ >+ >+static hda_nid_t alc260_dac_nids[1] = { >+ /* front */ >+ 0x02, >+}; >+ >+static hda_nid_t alc260_adc_nids[1] = { >+ /* ADC0 */ >+ 0x04, >+}; >+ >+static hda_nid_t alc260_adc_nids_alt[1] = { >+ /* ADC1 */ >+ 0x05, >+}; >+ >+static hda_nid_t alc260_hp_adc_nids[2] = { >+ /* ADC1, 0 */ >+ 0x05, 0x04 >+}; >+ >+/* NIDs used when simultaneous access to both ADCs makes sense. Note that >+ * alc260_capture_mixer assumes ADC0 (nid 0x04) is the first ADC. >+ */ >+static hda_nid_t alc260_dual_adc_nids[2] = { >+ /* ADC0, ADC1 */ >+ 0x04, 0x05 >+}; >+ >+#define ALC260_DIGOUT_NID 0x03 >+#define ALC260_DIGIN_NID 0x06 >+ >+static struct hda_input_mux alc260_capture_source = { >+ .num_items = 4, >+ .items = { >+ { "Mic", 0x0 }, >+ { "Front Mic", 0x1 }, >+ { "Line", 0x2 }, >+ { "CD", 0x4 }, >+ }, >+}; >+ >+/* On Fujitsu S702x laptops capture only makes sense from Mic/LineIn jack, >+ * headphone jack and the internal CD lines since these are the only pins at >+ * which audio can appear. For flexibility, also allow the option of >+ * recording the mixer output on the second ADC (ADC0 doesn't have a >+ * connection to the mixer output). >+ */ >+static struct hda_input_mux alc260_fujitsu_capture_sources[2] = { >+ { >+ .num_items = 3, >+ .items = { >+ { "Mic/Line", 0x0 }, >+ { "CD", 0x4 }, >+ { "Headphone", 0x2 }, >+ }, >+ }, >+ { >+ .num_items = 4, >+ .items = { >+ { "Mic/Line", 0x0 }, >+ { "CD", 0x4 }, >+ { "Headphone", 0x2 }, >+ { "Mixer", 0x5 }, >+ }, >+ }, >+ >+}; >+ >+/* Acer TravelMate(/Extensa/Aspire) notebooks have similar configuration to >+ * the Fujitsu S702x, but jacks are marked differently. >+ */ >+static struct hda_input_mux alc260_acer_capture_sources[2] = { >+ { >+ .num_items = 4, >+ .items = { >+ { "Mic", 0x0 }, >+ { "Line", 0x2 }, >+ { "CD", 0x4 }, >+ { "Headphone", 0x5 }, >+ }, >+ }, >+ { >+ .num_items = 5, >+ .items = { >+ { "Mic", 0x0 }, >+ { "Line", 0x2 }, >+ { "CD", 0x4 }, >+ { "Headphone", 0x6 }, >+ { "Mixer", 0x5 }, >+ }, >+ }, >+}; >+/* >+ * This is just place-holder, so there's something for alc_build_pcms to look >+ * at when it calculates the maximum number of channels. ALC260 has no mixer >+ * element which allows changing the channel mode, so the verb list is >+ * never used. >+ */ >+static struct hda_channel_mode alc260_modes[1] = { >+ { 2, NULL }, >+}; >+ >+ >+/* Mixer combinations >+ * >+ * basic: base_output + input + pc_beep + capture >+ * HP: base_output + input + capture_alt >+ * HP_3013: hp_3013 + input + capture >+ * fujitsu: fujitsu + capture >+ * acer: acer + capture >+ */ >+ >+static struct snd_kcontrol_new alc260_base_output_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x08, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Front Playback Switch", 0x08, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x09, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Headphone Playback Switch", 0x09, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE_MONO("Mono Playback Switch", 0x0a, 1, 2, HDA_INPUT), >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc260_input_mixer[] = { >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x07, 0x01, HDA_INPUT), >+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x07, 0x01, HDA_INPUT), >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc260_pc_beep_mixer[] = { >+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x07, 0x05, HDA_INPUT), >+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x07, 0x05, HDA_INPUT), >+ { } /* end */ >+}; >+ >+/* update HP, line and mono out pins according to the master switch */ >+static void alc260_hp_master_update(struct hda_codec *codec, >+ hda_nid_t hp, hda_nid_t line, >+ hda_nid_t mono) >+{ >+ struct alc_spec *spec = codec->spec; >+ unsigned int val = spec->master_sw ? PIN_HP : 0; >+ /* change HP and line-out pins */ >+ snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, >+ val); >+ snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, >+ val); >+ /* mono (speaker) depending on the HP jack sense */ >+ val = (val && !spec->jack_present) ? PIN_OUT : 0; >+ snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, >+ val); >+} >+ >+static int alc260_hp_master_sw_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.integer.value = spec->master_sw; >+ return 0; >+} >+ >+static int alc260_hp_master_sw_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 val = !!*ucontrol->value.integer.value; >+ hda_nid_t hp, line, mono; >+ >+ if (val == spec->master_sw) >+ return 0; >+ spec->master_sw = val; >+ hp = (kcontrol->private_value >> 16) & 0xff; >+ line = (kcontrol->private_value >> 8) & 0xff; >+ mono = kcontrol->private_value & 0xff; >+ alc260_hp_master_update(codec, hp, line, mono); >+ return 1; >+} >+ >+static struct snd_kcontrol_new alc260_hp_output_mixer[] = { >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Master Playback Switch", >+ .info = snd_ctl_boolean_mono_info, >+ .get = alc260_hp_master_sw_get, >+ .put = alc260_hp_master_sw_put, >+ .private_value = (0x0f << 16) | (0x10 << 8) | 0x11 >+ }, >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x08, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Front Playback Switch", 0x08, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x09, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Headphone Playback Switch", 0x09, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0, >+ HDA_OUTPUT), >+ HDA_BIND_MUTE_MONO("Speaker Playback Switch", 0x0a, 1, 2, HDA_INPUT), >+ { } /* end */ >+}; >+ >+static struct hda_verb alc260_hp_unsol_verbs[] = { >+ {0x10, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, >+ {}, >+}; >+ >+static void alc260_hp_automute(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ unsigned int present; >+ >+ present = snd_hda_codec_read(codec, 0x10, 0, >+ AC_VERB_GET_PIN_SENSE, 0); >+ spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0; >+ alc260_hp_master_update(codec, 0x0f, 0x10, 0x11); >+} >+ >+static void alc260_hp_unsol_event(struct hda_codec *codec, unsigned int res) >+{ >+ if ((res >> 26) == ALC880_HP_EVENT) >+ alc260_hp_automute(codec); >+} >+ >+static struct snd_kcontrol_new alc260_hp_3013_mixer[] = { >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Master Playback Switch", >+ .info = snd_ctl_boolean_mono_info, >+ .get = alc260_hp_master_sw_get, >+ .put = alc260_hp_master_sw_put, >+ .private_value = (0x10 << 16) | (0x15 << 8) | 0x11 >+ }, >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x09, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Front Playback Switch", 0x10, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Aux-In Playback Volume", 0x07, 0x06, HDA_INPUT), >+ HDA_CODEC_MUTE("Aux-In Playback Switch", 0x07, 0x06, HDA_INPUT), >+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x11, 1, 0x0, HDA_OUTPUT), >+ { } /* end */ >+}; >+ >+static struct hda_verb alc260_hp_3013_unsol_verbs[] = { >+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, >+ {}, >+}; >+ >+static void alc260_hp_3013_automute(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ unsigned int present; >+ >+ present = snd_hda_codec_read(codec, 0x15, 0, >+ AC_VERB_GET_PIN_SENSE, 0); >+ spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0; >+ alc260_hp_master_update(codec, 0x10, 0x15, 0x11); >+} >+ >+static void alc260_hp_3013_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ if ((res >> 26) == ALC880_HP_EVENT) >+ alc260_hp_3013_automute(codec); >+} >+ >+/* Fujitsu S702x series laptops. ALC260 pin usage: Mic/Line jack = 0x12, >+ * HP jack = 0x14, CD audio = 0x16, internal speaker = 0x10. >+ */ >+static struct snd_kcontrol_new alc260_fujitsu_mixer[] = { >+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Headphone Playback Switch", 0x08, 2, HDA_INPUT), >+ ALC_PIN_MODE("Headphone Jack Mode", 0x14, ALC_PIN_DIR_INOUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic/Line Playback Volume", 0x07, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic/Line Playback Switch", 0x07, 0x0, HDA_INPUT), >+ ALC_PIN_MODE("Mic/Line Jack Mode", 0x12, ALC_PIN_DIR_IN), >+ HDA_CODEC_VOLUME("Beep Playback Volume", 0x07, 0x05, HDA_INPUT), >+ HDA_CODEC_MUTE("Beep Playback Switch", 0x07, 0x05, HDA_INPUT), >+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x09, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Speaker Playback Switch", 0x09, 2, HDA_INPUT), >+ { } /* end */ >+}; >+ >+/* Mixer for Acer TravelMate(/Extensa/Aspire) notebooks. Note that current >+ * versions of the ALC260 don't act on requests to enable mic bias from NID >+ * 0x0f (used to drive the headphone jack in these laptops). The ALC260 >+ * datasheet doesn't mention this restriction. At this stage it's not clear >+ * whether this behaviour is intentional or is a hardware bug in chip >+ * revisions available in early 2006. Therefore for now allow the >+ * "Headphone Jack Mode" control to span all choices, but if it turns out >+ * that the lack of mic bias for this NID is intentional we could change the >+ * mode from ALC_PIN_DIR_INOUT to ALC_PIN_DIR_INOUT_NOMICBIAS. >+ * >+ * In addition, Acer TravelMate(/Extensa/Aspire) notebooks in early 2006 >+ * don't appear to make the mic bias available from the "line" jack, even >+ * though the NID used for this jack (0x14) can supply it. The theory is >+ * that perhaps Acer have included blocking capacitors between the ALC260 >+ * and the output jack. If this turns out to be the case for all such >+ * models the "Line Jack Mode" mode could be changed from ALC_PIN_DIR_INOUT >+ * to ALC_PIN_DIR_INOUT_NOMICBIAS. >+ * >+ * The C20x Tablet series have a mono internal speaker which is controlled >+ * via the chip's Mono sum widget and pin complex, so include the necessary >+ * controls for such models. On models without a "mono speaker" the control >+ * won't do anything. >+ */ >+static struct snd_kcontrol_new alc260_acer_mixer[] = { >+ HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Master Playback Switch", 0x08, 2, HDA_INPUT), >+ ALC_PIN_MODE("Headphone Jack Mode", 0x0f, ALC_PIN_DIR_INOUT), >+ HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0, >+ HDA_OUTPUT), >+ HDA_BIND_MUTE_MONO("Speaker Playback Switch", 0x0a, 1, 2, >+ HDA_INPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT), >+ ALC_PIN_MODE("Mic Jack Mode", 0x12, ALC_PIN_DIR_IN), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT), >+ ALC_PIN_MODE("Line Jack Mode", 0x14, ALC_PIN_DIR_INOUT), >+ HDA_CODEC_VOLUME("Beep Playback Volume", 0x07, 0x05, HDA_INPUT), >+ HDA_CODEC_MUTE("Beep Playback Switch", 0x07, 0x05, HDA_INPUT), >+ { } /* end */ >+}; >+ >+/* Packard bell V7900 ALC260 pin usage: HP = 0x0f, Mic jack = 0x12, >+ * Line In jack = 0x14, CD audio = 0x16, pc beep = 0x17. >+ */ >+static struct snd_kcontrol_new alc260_will_mixer[] = { >+ HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Master Playback Switch", 0x08, 0x2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT), >+ ALC_PIN_MODE("Mic Jack Mode", 0x12, ALC_PIN_DIR_IN), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT), >+ ALC_PIN_MODE("Line Jack Mode", 0x14, ALC_PIN_DIR_INOUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Beep Playback Volume", 0x07, 0x05, HDA_INPUT), >+ HDA_CODEC_MUTE("Beep Playback Switch", 0x07, 0x05, HDA_INPUT), >+ { } /* end */ >+}; >+ >+/* Replacer 672V ALC260 pin usage: Mic jack = 0x12, >+ * Line In jack = 0x14, ATAPI Mic = 0x13, speaker = 0x0f. >+ */ >+static struct snd_kcontrol_new alc260_replacer_672v_mixer[] = { >+ HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Master Playback Switch", 0x08, 0x2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT), >+ ALC_PIN_MODE("Mic Jack Mode", 0x12, ALC_PIN_DIR_IN), >+ HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x07, 0x1, HDA_INPUT), >+ HDA_CODEC_MUTE("ATATI Mic Playback Switch", 0x07, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT), >+ ALC_PIN_MODE("Line Jack Mode", 0x14, ALC_PIN_DIR_INOUT), >+ { } /* end */ >+}; >+ >+/* capture mixer elements */ >+static struct snd_kcontrol_new alc260_capture_mixer[] = { >+ HDA_CODEC_VOLUME("Capture Volume", 0x04, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x04, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x05, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x05, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ /* The multiple "Capture Source" controls confuse alsamixer >+ * So call somewhat different.. >+ */ >+ /* .name = "Capture Source", */ >+ .name = "Input Source", >+ .count = 2, >+ .info = alc_mux_enum_info, >+ .get = alc_mux_enum_get, >+ .put = alc_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc260_capture_alt_mixer[] = { >+ HDA_CODEC_VOLUME("Capture Volume", 0x05, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x05, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ /* The multiple "Capture Source" controls confuse alsamixer >+ * So call somewhat different.. >+ */ >+ /* .name = "Capture Source", */ >+ .name = "Input Source", >+ .count = 1, >+ .info = alc_mux_enum_info, >+ .get = alc_mux_enum_get, >+ .put = alc_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+/* >+ * initialization verbs >+ */ >+static struct hda_verb alc260_init_verbs[] = { >+ /* Line In pin widget for input */ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ /* CD pin widget for input */ >+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ /* Mic1 (rear panel) pin widget for input and vref at 80% */ >+ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ /* Mic2 (front panel) pin widget for input and vref at 80% */ >+ {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ /* LINE-2 is used for line-out in rear */ >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ /* select line-out */ >+ {0x0e, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ /* LINE-OUT pin */ >+ {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ /* enable HP */ >+ {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ /* enable Mono */ >+ {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ /* mute capture amp left and right */ >+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ /* set connection select to line in (default select for this ADC) */ >+ {0x04, AC_VERB_SET_CONNECT_SEL, 0x02}, >+ /* mute capture amp left and right */ >+ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ /* set connection select to line in (default select for this ADC) */ >+ {0x05, AC_VERB_SET_CONNECT_SEL, 0x02}, >+ /* set vol=0 Line-Out mixer amp left and right */ >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ /* unmute pin widget amp left and right (no gain on this amp) */ >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ /* set vol=0 HP mixer amp left and right */ >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ /* unmute pin widget amp left and right (no gain on this amp) */ >+ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ /* set vol=0 Mono mixer amp left and right */ >+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ /* unmute pin widget amp left and right (no gain on this amp) */ >+ {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ /* unmute LINE-2 out pin */ >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & >+ * Line In 2 = 0x03 >+ */ >+ /* mute analog inputs */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, >+ /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */ >+ /* mute Front out path */ >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ /* mute Headphone out path */ >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ /* mute Mono out path */ >+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ { } >+}; >+ >+#if 0 /* should be identical with alc260_init_verbs? */ >+static struct hda_verb alc260_hp_init_verbs[] = { >+ /* Headphone and output */ >+ {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, >+ /* mono output */ >+ {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, >+ /* Mic1 (rear panel) pin widget for input and vref at 80% */ >+ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, >+ /* Mic2 (front panel) pin widget for input and vref at 80% */ >+ {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, >+ /* Line In pin widget for input */ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, >+ /* Line-2 pin widget for output */ >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, >+ /* CD pin widget for input */ >+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, >+ /* unmute amp left and right */ >+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000}, >+ /* set connection select to line in (default select for this ADC) */ >+ {0x04, AC_VERB_SET_CONNECT_SEL, 0x02}, >+ /* unmute Line-Out mixer amp left and right (volume = 0) */ >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, >+ /* mute pin widget amp left and right (no gain on this amp) */ >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, >+ /* unmute HP mixer amp left and right (volume = 0) */ >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, >+ /* mute pin widget amp left and right (no gain on this amp) */ >+ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, >+ /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & >+ * Line In 2 = 0x03 >+ */ >+ /* mute analog inputs */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, >+ /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */ >+ /* Unmute Front out path */ >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, >+ /* Unmute Headphone out path */ >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, >+ /* Unmute Mono out path */ >+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, >+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, >+ { } >+}; >+#endif >+ >+static struct hda_verb alc260_hp_3013_init_verbs[] = { >+ /* Line out and output */ >+ {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, >+ /* mono output */ >+ {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, >+ /* Mic1 (rear panel) pin widget for input and vref at 80% */ >+ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, >+ /* Mic2 (front panel) pin widget for input and vref at 80% */ >+ {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, >+ /* Line In pin widget for input */ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, >+ /* Headphone pin widget for output */ >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, >+ /* CD pin widget for input */ >+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, >+ /* unmute amp left and right */ >+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000}, >+ /* set connection select to line in (default select for this ADC) */ >+ {0x04, AC_VERB_SET_CONNECT_SEL, 0x02}, >+ /* unmute Line-Out mixer amp left and right (volume = 0) */ >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, >+ /* mute pin widget amp left and right (no gain on this amp) */ >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, >+ /* unmute HP mixer amp left and right (volume = 0) */ >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, >+ /* mute pin widget amp left and right (no gain on this amp) */ >+ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, >+ /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & >+ * Line In 2 = 0x03 >+ */ >+ /* mute analog inputs */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, >+ /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */ >+ /* Unmute Front out path */ >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, >+ /* Unmute Headphone out path */ >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, >+ /* Unmute Mono out path */ >+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, >+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, >+ { } >+}; >+ >+/* Initialisation sequence for ALC260 as configured in Fujitsu S702x >+ * laptops. ALC260 pin usage: Mic/Line jack = 0x12, HP jack = 0x14, CD >+ * audio = 0x16, internal speaker = 0x10. >+ */ >+static struct hda_verb alc260_fujitsu_init_verbs[] = { >+ /* Disable all GPIOs */ >+ {0x01, AC_VERB_SET_GPIO_MASK, 0}, >+ /* Internal speaker is connected to headphone pin */ >+ {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ /* Headphone/Line-out jack connects to Line1 pin; make it an output */ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ /* Mic/Line-in jack is connected to mic1 pin, so make it an input */ >+ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ /* Ensure all other unused pins are disabled and muted. */ >+ {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, >+ {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, >+ {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ >+ /* Disable digital (SPDIF) pins */ >+ {0x03, AC_VERB_SET_DIGI_CONVERT_1, 0}, >+ {0x06, AC_VERB_SET_DIGI_CONVERT_1, 0}, >+ >+ /* Ensure Line1 pin widget takes its input from the OUT1 sum bus >+ * when acting as an output. >+ */ >+ {0x0d, AC_VERB_SET_CONNECT_SEL, 0}, >+ >+ /* Start with output sum widgets muted and their output gains at min */ >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ >+ /* Unmute HP pin widget amp left and right (no equiv mixer ctrl) */ >+ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ /* Unmute Line1 pin widget output buffer since it starts as an output. >+ * If 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. >+ * Therefore there's no need to enable the input buffer at this >+ * stage. >+ */ >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ /* Unmute input buffer of pin widget used for Line-in (no equiv >+ * mixer ctrl) >+ */ >+ {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ >+ /* Mute capture amp left and right */ >+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ /* Set ADC connection select to match default mixer setting - line >+ * in (on mic1 pin) >+ */ >+ {0x04, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ >+ /* Do the same for the second ADC: mute capture input amp and >+ * set ADC connection to line in (on mic1 pin) >+ */ >+ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x05, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ >+ /* Mute all inputs to mixer widget (even unconnected ones) */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */ >+ >+ { } >+}; >+ >+/* Initialisation sequence for ALC260 as configured in Acer TravelMate and >+ * similar laptops (adapted from Fujitsu init verbs). >+ */ >+static struct hda_verb alc260_acer_init_verbs[] = { >+ /* On TravelMate laptops, GPIO 0 enables the internal speaker and >+ * the headphone jack. Turn this on and rely on the standard mute >+ * methods whenever the user wants to turn these outputs off. >+ */ >+ {0x01, AC_VERB_SET_GPIO_MASK, 0x01}, >+ {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01}, >+ {0x01, AC_VERB_SET_GPIO_DATA, 0x01}, >+ /* Internal speaker/Headphone jack is connected to Line-out pin */ >+ {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ /* Internal microphone/Mic jack is connected to Mic1 pin */ >+ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50}, >+ /* Line In jack is connected to Line1 pin */ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ /* Some Acers (eg: C20x Tablets) use Mono pin for internal speaker */ >+ {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ /* Ensure all other unused pins are disabled and muted. */ >+ {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, >+ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, >+ {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ /* Disable digital (SPDIF) pins */ >+ {0x03, AC_VERB_SET_DIGI_CONVERT_1, 0}, >+ {0x06, AC_VERB_SET_DIGI_CONVERT_1, 0}, >+ >+ /* Ensure Mic1 and Line1 pin widgets take input from the OUT1 sum >+ * bus when acting as outputs. >+ */ >+ {0x0b, AC_VERB_SET_CONNECT_SEL, 0}, >+ {0x0d, AC_VERB_SET_CONNECT_SEL, 0}, >+ >+ /* Start with output sum widgets muted and their output gains at min */ >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ >+ /* Unmute Line-out pin widget amp left and right >+ * (no equiv mixer ctrl) >+ */ >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ /* Unmute mono pin widget amp output (no equiv mixer ctrl) */ >+ {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ /* Unmute Mic1 and Line1 pin widget input buffers since they start as >+ * inputs. If 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. >+ * Therefore there's no need to enable the input buffer at this >+ * stage. >+ */ >+ {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ >+ /* Mute capture amp left and right */ >+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ /* Set ADC connection select to match default mixer setting - mic >+ * (on mic1 pin) >+ */ >+ {0x04, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ >+ /* Do similar with the second ADC: mute capture input amp and >+ * set ADC connection to mic to match ALSA's default state. >+ */ >+ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x05, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ >+ /* Mute all inputs to mixer widget (even unconnected ones) */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */ >+ >+ { } >+}; >+ >+static struct hda_verb alc260_will_verbs[] = { >+ {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x0b, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x0f, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, >+ {0x1a, AC_VERB_SET_COEF_INDEX, 0x07}, >+ {0x1a, AC_VERB_SET_PROC_COEF, 0x3040}, >+ {} >+}; >+ >+static struct hda_verb alc260_replacer_672v_verbs[] = { >+ {0x0f, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, >+ {0x1a, AC_VERB_SET_COEF_INDEX, 0x07}, >+ {0x1a, AC_VERB_SET_PROC_COEF, 0x3050}, >+ >+ {0x01, AC_VERB_SET_GPIO_MASK, 0x01}, >+ {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01}, >+ {0x01, AC_VERB_SET_GPIO_DATA, 0x00}, >+ >+ {0x0f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, >+ {} >+}; >+ >+/* toggle speaker-output according to the hp-jack state */ >+static void alc260_replacer_672v_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ >+ /* speaker --> GPIO Data 0, hp or spdif --> GPIO data 1 */ >+ present = snd_hda_codec_read(codec, 0x0f, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ if (present) { >+ snd_hda_codec_write_cache(codec, 0x01, 0, >+ AC_VERB_SET_GPIO_DATA, 1); >+ snd_hda_codec_write_cache(codec, 0x0f, 0, >+ AC_VERB_SET_PIN_WIDGET_CONTROL, >+ PIN_HP); >+ } else { >+ snd_hda_codec_write_cache(codec, 0x01, 0, >+ AC_VERB_SET_GPIO_DATA, 0); >+ snd_hda_codec_write_cache(codec, 0x0f, 0, >+ AC_VERB_SET_PIN_WIDGET_CONTROL, >+ PIN_OUT); >+ } >+} >+ >+static void alc260_replacer_672v_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ if ((res >> 26) == ALC880_HP_EVENT) >+ alc260_replacer_672v_automute(codec); >+} >+ >+/* Test configuration for debugging, modelled after the ALC880 test >+ * configuration. >+ */ >+#ifdef CONFIG_SND_DEBUG >+static hda_nid_t alc260_test_dac_nids[1] = { >+ 0x02, >+}; >+static hda_nid_t alc260_test_adc_nids[2] = { >+ 0x04, 0x05, >+}; >+/* For testing the ALC260, each input MUX needs its own definition since >+ * the signal assignments are different. This assumes that the first ADC >+ * is NID 0x04. >+ */ >+static struct hda_input_mux alc260_test_capture_sources[2] = { >+ { >+ .num_items = 7, >+ .items = { >+ { "MIC1 pin", 0x0 }, >+ { "MIC2 pin", 0x1 }, >+ { "LINE1 pin", 0x2 }, >+ { "LINE2 pin", 0x3 }, >+ { "CD pin", 0x4 }, >+ { "LINE-OUT pin", 0x5 }, >+ { "HP-OUT pin", 0x6 }, >+ }, >+ }, >+ { >+ .num_items = 8, >+ .items = { >+ { "MIC1 pin", 0x0 }, >+ { "MIC2 pin", 0x1 }, >+ { "LINE1 pin", 0x2 }, >+ { "LINE2 pin", 0x3 }, >+ { "CD pin", 0x4 }, >+ { "Mixer", 0x5 }, >+ { "LINE-OUT pin", 0x6 }, >+ { "HP-OUT pin", 0x7 }, >+ }, >+ }, >+}; >+static struct snd_kcontrol_new alc260_test_mixer[] = { >+ /* Output driver widgets */ >+ HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE_MONO("Mono Playback Switch", 0x0a, 1, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("LOUT2 Playback Volume", 0x09, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("LOUT2 Playback Switch", 0x09, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("LOUT1 Playback Volume", 0x08, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("LOUT1 Playback Switch", 0x08, 2, HDA_INPUT), >+ >+ /* Modes for retasking pin widgets >+ * Note: the ALC260 doesn't seem to act on requests to enable mic >+ * bias from NIDs 0x0f and 0x10. The ALC260 datasheet doesn't >+ * mention this restriction. At this stage it's not clear whether >+ * this behaviour is intentional or is a hardware bug in chip >+ * revisions available at least up until early 2006. Therefore for >+ * now allow the "HP-OUT" and "LINE-OUT" Mode controls to span all >+ * choices, but if it turns out that the lack of mic bias for these >+ * NIDs is intentional we could change their modes from >+ * ALC_PIN_DIR_INOUT to ALC_PIN_DIR_INOUT_NOMICBIAS. >+ */ >+ ALC_PIN_MODE("HP-OUT pin mode", 0x10, ALC_PIN_DIR_INOUT), >+ ALC_PIN_MODE("LINE-OUT pin mode", 0x0f, ALC_PIN_DIR_INOUT), >+ ALC_PIN_MODE("LINE2 pin mode", 0x15, ALC_PIN_DIR_INOUT), >+ ALC_PIN_MODE("LINE1 pin mode", 0x14, ALC_PIN_DIR_INOUT), >+ ALC_PIN_MODE("MIC2 pin mode", 0x13, ALC_PIN_DIR_INOUT), >+ ALC_PIN_MODE("MIC1 pin mode", 0x12, ALC_PIN_DIR_INOUT), >+ >+ /* Loopback mixer controls */ >+ HDA_CODEC_VOLUME("MIC1 Playback Volume", 0x07, 0x00, HDA_INPUT), >+ HDA_CODEC_MUTE("MIC1 Playback Switch", 0x07, 0x00, HDA_INPUT), >+ HDA_CODEC_VOLUME("MIC2 Playback Volume", 0x07, 0x01, HDA_INPUT), >+ HDA_CODEC_MUTE("MIC2 Playback Switch", 0x07, 0x01, HDA_INPUT), >+ HDA_CODEC_VOLUME("LINE1 Playback Volume", 0x07, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("LINE1 Playback Switch", 0x07, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("LINE2 Playback Volume", 0x07, 0x03, HDA_INPUT), >+ HDA_CODEC_MUTE("LINE2 Playback Switch", 0x07, 0x03, HDA_INPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Beep Playback Volume", 0x07, 0x05, HDA_INPUT), >+ HDA_CODEC_MUTE("Beep Playback Switch", 0x07, 0x05, HDA_INPUT), >+ HDA_CODEC_VOLUME("LINE-OUT loopback Playback Volume", 0x07, 0x06, HDA_INPUT), >+ HDA_CODEC_MUTE("LINE-OUT loopback Playback Switch", 0x07, 0x06, HDA_INPUT), >+ HDA_CODEC_VOLUME("HP-OUT loopback Playback Volume", 0x07, 0x7, HDA_INPUT), >+ HDA_CODEC_MUTE("HP-OUT loopback Playback Switch", 0x07, 0x7, HDA_INPUT), >+ >+ /* Controls for GPIO pins, assuming they are configured as outputs */ >+ ALC_GPIO_DATA_SWITCH("GPIO pin 0", 0x01, 0x01), >+ ALC_GPIO_DATA_SWITCH("GPIO pin 1", 0x01, 0x02), >+ ALC_GPIO_DATA_SWITCH("GPIO pin 2", 0x01, 0x04), >+ ALC_GPIO_DATA_SWITCH("GPIO pin 3", 0x01, 0x08), >+ >+ /* Switches to allow the digital IO pins to be enabled. The datasheet >+ * is ambigious as to which NID is which; testing on laptops which >+ * make this output available should provide clarification. >+ */ >+ ALC_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x03, 0x01), >+ ALC_SPDIF_CTRL_SWITCH("SPDIF Capture Switch", 0x06, 0x01), >+ >+ /* A switch allowing EAPD to be enabled. Some laptops seem to use >+ * this output to turn on an external amplifier. >+ */ >+ ALC_EAPD_CTRL_SWITCH("LINE-OUT EAPD Enable Switch", 0x0f, 0x02), >+ ALC_EAPD_CTRL_SWITCH("HP-OUT EAPD Enable Switch", 0x10, 0x02), >+ >+ { } /* end */ >+}; >+static struct hda_verb alc260_test_init_verbs[] = { >+ /* Enable all GPIOs as outputs with an initial value of 0 */ >+ {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x0f}, >+ {0x01, AC_VERB_SET_GPIO_DATA, 0x00}, >+ {0x01, AC_VERB_SET_GPIO_MASK, 0x0f}, >+ >+ /* Enable retasking pins as output, initially without power amp */ >+ {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {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}, >+ {0x12, 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. >+ */ >+ {0x03, AC_VERB_SET_DIGI_CONVERT_1, 0}, >+ {0x06, AC_VERB_SET_DIGI_CONVERT_1, 0}, >+ >+ /* Ensure mic1, mic2, line1 and line2 pin widgets take input from the >+ * OUT1 sum bus when acting as an output. >+ */ >+ {0x0b, AC_VERB_SET_CONNECT_SEL, 0}, >+ {0x0c, AC_VERB_SET_CONNECT_SEL, 0}, >+ {0x0d, AC_VERB_SET_CONNECT_SEL, 0}, >+ {0x0e, AC_VERB_SET_CONNECT_SEL, 0}, >+ >+ /* Start with output sum widgets muted and their output gains at min */ >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ >+ /* 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. >+ */ >+ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {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}, >+ {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ /* Also unmute the mono-out pin widget */ >+ {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ >+ /* Mute capture amp left and right */ >+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ /* Set ADC connection select to match default mixer setting (mic1 >+ * pin) >+ */ >+ {0x04, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ >+ /* Do the same for the second ADC: mute capture input amp and >+ * set ADC connection to mic1 pin >+ */ >+ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x05, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ >+ /* Mute all inputs to mixer widget (even unconnected ones) */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */ >+ >+ { } >+}; >+#endif >+ >+#define alc260_pcm_analog_playback alc880_pcm_analog_alt_playback >+#define alc260_pcm_analog_capture alc880_pcm_analog_capture >+ >+#define alc260_pcm_digital_playback alc880_pcm_digital_playback >+#define alc260_pcm_digital_capture alc880_pcm_digital_capture >+ >+/* >+ * for BIOS auto-configuration >+ */ >+ >+static int alc260_add_playback_controls(struct alc_spec *spec, hda_nid_t nid, >+ const char *pfx) >+{ >+ hda_nid_t nid_vol; >+ unsigned long vol_val, sw_val; >+ char name[32]; >+ int err; >+ >+ if (nid >= 0x0f && nid < 0x11) { >+ nid_vol = nid - 0x7; >+ vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT); >+ sw_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); >+ } else if (nid == 0x11) { >+ nid_vol = nid - 0x7; >+ vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT); >+ sw_val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT); >+ } else if (nid >= 0x12 && nid <= 0x15) { >+ nid_vol = 0x08; >+ vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT); >+ sw_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); >+ } else >+ return 0; /* N/A */ >+ >+ snprintf(name, sizeof(name), "%s Playback Volume", pfx); >+ err = add_control(spec, ALC_CTL_WIDGET_VOL, name, vol_val); >+ if (err < 0) >+ return err; >+ snprintf(name, sizeof(name), "%s Playback Switch", pfx); >+ err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, sw_val); >+ if (err < 0) >+ return err; >+ return 1; >+} >+ >+/* add playback controls from the parsed DAC table */ >+static int alc260_auto_create_multi_out_ctls(struct alc_spec *spec, >+ const struct auto_pin_cfg *cfg) >+{ >+ hda_nid_t nid; >+ int err; >+ >+ spec->multiout.num_dacs = 1; >+ spec->multiout.dac_nids = spec->private_dac_nids; >+ spec->multiout.dac_nids[0] = 0x02; >+ >+ nid = cfg->line_out_pins[0]; >+ if (nid) { >+ err = alc260_add_playback_controls(spec, nid, "Front"); >+ if (err < 0) >+ return err; >+ } >+ >+ nid = cfg->speaker_pins[0]; >+ if (nid) { >+ err = alc260_add_playback_controls(spec, nid, "Speaker"); >+ if (err < 0) >+ return err; >+ } >+ >+ nid = cfg->hp_pins[0]; >+ if (nid) { >+ err = alc260_add_playback_controls(spec, nid, "Headphone"); >+ if (err < 0) >+ return err; >+ } >+ return 0; >+} >+ >+/* create playback/capture controls for input pins */ >+static int alc260_auto_create_analog_input_ctls(struct alc_spec *spec, >+ const struct auto_pin_cfg *cfg) >+{ >+ struct hda_input_mux *imux = &spec->private_imux; >+ int i, err, idx; >+ >+ for (i = 0; i < AUTO_PIN_LAST; i++) { >+ if (cfg->input_pins[i] >= 0x12) { >+ idx = cfg->input_pins[i] - 0x12; >+ err = new_analog_input(spec, cfg->input_pins[i], >+ auto_pin_cfg_labels[i], idx, >+ 0x07); >+ if (err < 0) >+ return err; >+ imux->items[imux->num_items].label = >+ auto_pin_cfg_labels[i]; >+ imux->items[imux->num_items].index = idx; >+ imux->num_items++; >+ } >+ if (cfg->input_pins[i] >= 0x0f && cfg->input_pins[i] <= 0x10){ >+ idx = cfg->input_pins[i] - 0x09; >+ err = new_analog_input(spec, cfg->input_pins[i], >+ auto_pin_cfg_labels[i], idx, >+ 0x07); >+ if (err < 0) >+ return err; >+ imux->items[imux->num_items].label = >+ auto_pin_cfg_labels[i]; >+ imux->items[imux->num_items].index = idx; >+ imux->num_items++; >+ } >+ } >+ return 0; >+} >+ >+static void alc260_auto_set_output_and_unmute(struct hda_codec *codec, >+ hda_nid_t nid, int pin_type, >+ int sel_idx) >+{ >+ alc_set_pin_output(codec, nid, pin_type); >+ /* need the manual connection? */ >+ if (nid >= 0x12) { >+ int idx = nid - 0x12; >+ snd_hda_codec_write(codec, idx + 0x0b, 0, >+ AC_VERB_SET_CONNECT_SEL, sel_idx); >+ } >+} >+ >+static void alc260_auto_init_multi_out(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ hda_nid_t nid; >+ >+ alc_subsystem_id(codec, 0x10, 0x15, 0x0f); >+ nid = spec->autocfg.line_out_pins[0]; >+ if (nid) { >+ int pin_type = get_pin_type(spec->autocfg.line_out_type); >+ alc260_auto_set_output_and_unmute(codec, nid, pin_type, 0); >+ } >+ >+ nid = spec->autocfg.speaker_pins[0]; >+ if (nid) >+ alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0); >+ >+ nid = spec->autocfg.hp_pins[0]; >+ if (nid) >+ alc260_auto_set_output_and_unmute(codec, nid, PIN_HP, 0); >+} >+ >+#define ALC260_PIN_CD_NID 0x16 >+static void alc260_auto_init_analog_input(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ int i; >+ >+ for (i = 0; i < AUTO_PIN_LAST; i++) { >+ hda_nid_t nid = spec->autocfg.input_pins[i]; >+ if (nid >= 0x12) { >+ snd_hda_codec_write(codec, nid, 0, >+ AC_VERB_SET_PIN_WIDGET_CONTROL, >+ i <= AUTO_PIN_FRONT_MIC ? >+ PIN_VREF80 : PIN_IN); >+ if (nid != ALC260_PIN_CD_NID) >+ snd_hda_codec_write(codec, nid, 0, >+ AC_VERB_SET_AMP_GAIN_MUTE, >+ AMP_OUT_MUTE); >+ } >+ } >+} >+ >+/* >+ * generic initialization of ADC, input mixers and output mixers >+ */ >+static struct hda_verb alc260_volume_init_verbs[] = { >+ /* >+ * Unmute ADC0-1 and set the default input to mic-in >+ */ >+ {0x04, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x05, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ >+ /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback >+ * mixer widget >+ * Note: PASD motherboards uses the Line In 2 as the input for >+ * front panel mic (mic 2) >+ */ >+ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ >+ /* mute analog inputs */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, >+ >+ /* >+ * Set up output mixers (0x08 - 0x0a) >+ */ >+ /* set vol=0 to output mixers */ >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ /* set up input amps for analog loopback */ >+ /* Amp Indices: DAC = 0, mixer = 1 */ >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ >+ { } >+}; >+ >+static int alc260_parse_auto_config(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ unsigned int wcap; >+ int err; >+ static hda_nid_t alc260_ignore[] = { 0x17, 0 }; >+ >+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, >+ alc260_ignore); >+ if (err < 0) >+ return err; >+ err = alc260_auto_create_multi_out_ctls(spec, &spec->autocfg); >+ if (err < 0) >+ return err; >+ if (!spec->kctl_alloc) >+ return 0; /* can't find valid BIOS pin config */ >+ err = alc260_auto_create_analog_input_ctls(spec, &spec->autocfg); >+ if (err < 0) >+ return err; >+ >+ spec->multiout.max_channels = 2; >+ >+ if (spec->autocfg.dig_out_pin) >+ spec->multiout.dig_out_nid = ALC260_DIGOUT_NID; >+ if (spec->kctl_alloc) >+ spec->mixers[spec->num_mixers++] = spec->kctl_alloc; >+ >+ spec->init_verbs[spec->num_init_verbs++] = alc260_volume_init_verbs; >+ >+ spec->num_mux_defs = 1; >+ spec->input_mux = &spec->private_imux; >+ >+ /* check whether NID 0x04 is valid */ >+ wcap = get_wcaps(codec, 0x04); >+ wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; /* get type */ >+ if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) { >+ spec->adc_nids = alc260_adc_nids_alt; >+ spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt); >+ spec->mixers[spec->num_mixers] = alc260_capture_alt_mixer; >+ } else { >+ spec->adc_nids = alc260_adc_nids; >+ spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids); >+ spec->mixers[spec->num_mixers] = alc260_capture_mixer; >+ } >+ spec->num_mixers++; >+ >+ return 1; >+} >+ >+/* additional initialization for auto-configuration model */ >+static void alc260_auto_init(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ alc260_auto_init_multi_out(codec); >+ alc260_auto_init_analog_input(codec); >+ if (spec->unsol_event) >+ alc_sku_automute(codec); >+} >+ >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+static struct hda_amp_list alc260_loopbacks[] = { >+ { 0x07, HDA_INPUT, 0 }, >+ { 0x07, HDA_INPUT, 1 }, >+ { 0x07, HDA_INPUT, 2 }, >+ { 0x07, HDA_INPUT, 3 }, >+ { 0x07, HDA_INPUT, 4 }, >+ { } /* end */ >+}; >+#endif >+ >+/* >+ * ALC260 configurations >+ */ >+static const char *alc260_models[ALC260_MODEL_LAST] = { >+ [ALC260_BASIC] = "basic", >+ [ALC260_HP] = "hp", >+ [ALC260_HP_3013] = "hp-3013", >+ [ALC260_FUJITSU_S702X] = "fujitsu", >+ [ALC260_ACER] = "acer", >+ [ALC260_WILL] = "will", >+ [ALC260_REPLACER_672V] = "replacer", >+#ifdef CONFIG_SND_DEBUG >+ [ALC260_TEST] = "test", >+#endif >+ [ALC260_AUTO] = "auto", >+}; >+ >+static struct snd_pci_quirk alc260_cfg_tbl[] = { >+ SND_PCI_QUIRK(0x1025, 0x007b, "Acer C20x", ALC260_ACER), >+ SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_ACER), >+ SND_PCI_QUIRK(0x103c, 0x2808, "HP d5700", ALC260_HP_3013), >+ SND_PCI_QUIRK(0x103c, 0x280a, "HP d5750", ALC260_HP_3013), >+ SND_PCI_QUIRK(0x103c, 0x3010, "HP", ALC260_HP_3013), >+ SND_PCI_QUIRK(0x103c, 0x3011, "HP", ALC260_HP), >+ SND_PCI_QUIRK(0x103c, 0x3012, "HP", ALC260_HP_3013), >+ SND_PCI_QUIRK(0x103c, 0x3013, "HP", ALC260_HP_3013), >+ SND_PCI_QUIRK(0x103c, 0x3014, "HP", ALC260_HP), >+ SND_PCI_QUIRK(0x103c, 0x3015, "HP", ALC260_HP), >+ SND_PCI_QUIRK(0x103c, 0x3016, "HP", ALC260_HP), >+ SND_PCI_QUIRK(0x104d, 0x81bb, "Sony VAIO", ALC260_BASIC), >+ SND_PCI_QUIRK(0x104d, 0x81cc, "Sony VAIO", ALC260_BASIC), >+ SND_PCI_QUIRK(0x104d, 0x81cd, "Sony VAIO", ALC260_BASIC), >+ SND_PCI_QUIRK(0x10cf, 0x1326, "Fujitsu S702X", ALC260_FUJITSU_S702X), >+ SND_PCI_QUIRK(0x152d, 0x0729, "CTL U553W", ALC260_BASIC), >+ SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V", ALC260_REPLACER_672V), >+ SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_WILL), >+ {} >+}; >+ >+static struct alc_config_preset alc260_presets[] = { >+ [ALC260_BASIC] = { >+ .mixers = { alc260_base_output_mixer, >+ alc260_input_mixer, >+ alc260_pc_beep_mixer, >+ alc260_capture_mixer }, >+ .init_verbs = { alc260_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc260_dac_nids), >+ .dac_nids = alc260_dac_nids, >+ .num_adc_nids = ARRAY_SIZE(alc260_adc_nids), >+ .adc_nids = alc260_adc_nids, >+ .num_channel_mode = ARRAY_SIZE(alc260_modes), >+ .channel_mode = alc260_modes, >+ .input_mux = &alc260_capture_source, >+ }, >+ [ALC260_HP] = { >+ .mixers = { alc260_hp_output_mixer, >+ alc260_input_mixer, >+ alc260_capture_alt_mixer }, >+ .init_verbs = { alc260_init_verbs, >+ alc260_hp_unsol_verbs }, >+ .num_dacs = ARRAY_SIZE(alc260_dac_nids), >+ .dac_nids = alc260_dac_nids, >+ .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids), >+ .adc_nids = alc260_hp_adc_nids, >+ .num_channel_mode = ARRAY_SIZE(alc260_modes), >+ .channel_mode = alc260_modes, >+ .input_mux = &alc260_capture_source, >+ .unsol_event = alc260_hp_unsol_event, >+ .init_hook = alc260_hp_automute, >+ }, >+ [ALC260_HP_3013] = { >+ .mixers = { alc260_hp_3013_mixer, >+ alc260_input_mixer, >+ alc260_capture_alt_mixer }, >+ .init_verbs = { alc260_hp_3013_init_verbs, >+ alc260_hp_3013_unsol_verbs }, >+ .num_dacs = ARRAY_SIZE(alc260_dac_nids), >+ .dac_nids = alc260_dac_nids, >+ .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids), >+ .adc_nids = alc260_hp_adc_nids, >+ .num_channel_mode = ARRAY_SIZE(alc260_modes), >+ .channel_mode = alc260_modes, >+ .input_mux = &alc260_capture_source, >+ .unsol_event = alc260_hp_3013_unsol_event, >+ .init_hook = alc260_hp_3013_automute, >+ }, >+ [ALC260_FUJITSU_S702X] = { >+ .mixers = { alc260_fujitsu_mixer, >+ alc260_capture_mixer }, >+ .init_verbs = { alc260_fujitsu_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc260_dac_nids), >+ .dac_nids = alc260_dac_nids, >+ .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids), >+ .adc_nids = alc260_dual_adc_nids, >+ .num_channel_mode = ARRAY_SIZE(alc260_modes), >+ .channel_mode = alc260_modes, >+ .num_mux_defs = ARRAY_SIZE(alc260_fujitsu_capture_sources), >+ .input_mux = alc260_fujitsu_capture_sources, >+ }, >+ [ALC260_ACER] = { >+ .mixers = { alc260_acer_mixer, >+ alc260_capture_mixer }, >+ .init_verbs = { alc260_acer_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc260_dac_nids), >+ .dac_nids = alc260_dac_nids, >+ .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids), >+ .adc_nids = alc260_dual_adc_nids, >+ .num_channel_mode = ARRAY_SIZE(alc260_modes), >+ .channel_mode = alc260_modes, >+ .num_mux_defs = ARRAY_SIZE(alc260_acer_capture_sources), >+ .input_mux = alc260_acer_capture_sources, >+ }, >+ [ALC260_WILL] = { >+ .mixers = { alc260_will_mixer, >+ alc260_capture_mixer }, >+ .init_verbs = { alc260_init_verbs, alc260_will_verbs }, >+ .num_dacs = ARRAY_SIZE(alc260_dac_nids), >+ .dac_nids = alc260_dac_nids, >+ .num_adc_nids = ARRAY_SIZE(alc260_adc_nids), >+ .adc_nids = alc260_adc_nids, >+ .dig_out_nid = ALC260_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc260_modes), >+ .channel_mode = alc260_modes, >+ .input_mux = &alc260_capture_source, >+ }, >+ [ALC260_REPLACER_672V] = { >+ .mixers = { alc260_replacer_672v_mixer, >+ alc260_capture_mixer }, >+ .init_verbs = { alc260_init_verbs, alc260_replacer_672v_verbs }, >+ .num_dacs = ARRAY_SIZE(alc260_dac_nids), >+ .dac_nids = alc260_dac_nids, >+ .num_adc_nids = ARRAY_SIZE(alc260_adc_nids), >+ .adc_nids = alc260_adc_nids, >+ .dig_out_nid = ALC260_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc260_modes), >+ .channel_mode = alc260_modes, >+ .input_mux = &alc260_capture_source, >+ .unsol_event = alc260_replacer_672v_unsol_event, >+ .init_hook = alc260_replacer_672v_automute, >+ }, >+#ifdef CONFIG_SND_DEBUG >+ [ALC260_TEST] = { >+ .mixers = { alc260_test_mixer, >+ alc260_capture_mixer }, >+ .init_verbs = { alc260_test_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc260_test_dac_nids), >+ .dac_nids = alc260_test_dac_nids, >+ .num_adc_nids = ARRAY_SIZE(alc260_test_adc_nids), >+ .adc_nids = alc260_test_adc_nids, >+ .num_channel_mode = ARRAY_SIZE(alc260_modes), >+ .channel_mode = alc260_modes, >+ .num_mux_defs = ARRAY_SIZE(alc260_test_capture_sources), >+ .input_mux = alc260_test_capture_sources, >+ }, >+#endif >+}; >+ >+static int patch_alc260(struct hda_codec *codec) >+{ >+ struct alc_spec *spec; >+ int err, board_config; >+ >+ spec = kzalloc(sizeof(*spec), GFP_KERNEL); >+ if (spec == NULL) >+ return -ENOMEM; >+ >+ codec->spec = spec; >+ >+ board_config = snd_hda_check_board_config(codec, ALC260_MODEL_LAST, >+ alc260_models, >+ alc260_cfg_tbl); >+ if (board_config < 0) { >+ snd_printd(KERN_INFO "hda_codec: Unknown model for ALC260, " >+ "trying auto-probe from BIOS...\n"); >+ board_config = ALC260_AUTO; >+ } >+ >+ if (board_config == ALC260_AUTO) { >+ /* automatic parse from the BIOS config */ >+ err = alc260_parse_auto_config(codec); >+ if (err < 0) { >+ alc_free(codec); >+ return err; >+ } else if (!err) { >+ printk(KERN_INFO >+ "hda_codec: Cannot set up configuration " >+ "from BIOS. Using base mode...\n"); >+ board_config = ALC260_BASIC; >+ } >+ } >+ >+ if (board_config != ALC260_AUTO) >+ setup_preset(spec, &alc260_presets[board_config]); >+ >+ spec->stream_name_analog = "ALC260 Analog"; >+ spec->stream_analog_playback = &alc260_pcm_analog_playback; >+ spec->stream_analog_capture = &alc260_pcm_analog_capture; >+ >+ spec->stream_name_digital = "ALC260 Digital"; >+ spec->stream_digital_playback = &alc260_pcm_digital_playback; >+ spec->stream_digital_capture = &alc260_pcm_digital_capture; >+ >+ spec->vmaster_nid = 0x08; >+ >+ codec->patch_ops = alc_patch_ops; >+ if (board_config == ALC260_AUTO) >+ spec->init_hook = alc260_auto_init; >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+ if (!spec->loopback.amplist) >+ spec->loopback.amplist = alc260_loopbacks; >+#endif >+ >+ return 0; >+} >+ >+ >+/* >+ * ALC882 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). >+ */ >+#define ALC882_DIGOUT_NID 0x06 >+#define ALC882_DIGIN_NID 0x0a >+ >+static struct hda_channel_mode alc882_ch_modes[1] = { >+ { 8, NULL } >+}; >+ >+static hda_nid_t alc882_dac_nids[4] = { >+ /* front, rear, clfe, rear_surr */ >+ 0x02, 0x03, 0x04, 0x05 >+}; >+ >+/* identical with ALC880 */ >+#define alc882_adc_nids alc880_adc_nids >+#define alc882_adc_nids_alt alc880_adc_nids_alt >+ >+static hda_nid_t alc882_capsrc_nids[3] = { 0x24, 0x23, 0x22 }; >+static hda_nid_t alc882_capsrc_nids_alt[2] = { 0x23, 0x22 }; >+ >+/* input MUX */ >+/* FIXME: should be a matrix-type input source selection */ >+ >+static struct hda_input_mux alc882_capture_source = { >+ .num_items = 4, >+ .items = { >+ { "Mic", 0x0 }, >+ { "Front Mic", 0x1 }, >+ { "Line", 0x2 }, >+ { "CD", 0x4 }, >+ }, >+}; >+#define alc882_mux_enum_info alc_mux_enum_info >+#define alc882_mux_enum_get alc_mux_enum_get >+ >+static int alc882_mux_enum_put(struct snd_kcontrol *kcontrol, >+ struct snd_ctl_elem_value *ucontrol) >+{ >+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); >+ struct alc_spec *spec = codec->spec; >+ const struct hda_input_mux *imux = spec->input_mux; >+ unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); >+ hda_nid_t nid = spec->capsrc_nids ? >+ spec->capsrc_nids[adc_idx] : spec->adc_nids[adc_idx]; >+ unsigned int *cur_val = &spec->cur_mux[adc_idx]; >+ unsigned int i, idx; >+ >+ idx = ucontrol->value.enumerated.item[0]; >+ if (idx >= imux->num_items) >+ idx = imux->num_items - 1; >+ if (*cur_val == idx) >+ return 0; >+ for (i = 0; i < imux->num_items; i++) { >+ unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE; >+ snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, >+ imux->items[i].index, >+ HDA_AMP_MUTE, v); >+ } >+ *cur_val = idx; >+ return 1; >+} >+ >+/* >+ * 2ch mode >+ */ >+static struct hda_verb alc882_3ST_ch2_init[] = { >+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, >+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, >+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, >+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, >+ { } /* end */ >+}; >+ >+/* >+ * 6ch mode >+ */ >+static struct hda_verb alc882_3ST_ch6_init[] = { >+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, >+ { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, >+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, >+ { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, >+ { } /* end */ >+}; >+ >+static struct hda_channel_mode alc882_3ST_6ch_modes[2] = { >+ { 2, alc882_3ST_ch2_init }, >+ { 6, alc882_3ST_ch6_init }, >+}; >+ >+/* >+ * 6ch mode >+ */ >+static struct hda_verb alc882_sixstack_ch6_init[] = { >+ { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, >+ { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { } /* end */ >+}; >+ >+/* >+ * 8ch mode >+ */ >+static struct hda_verb alc882_sixstack_ch8_init[] = { >+ { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { } /* end */ >+}; >+ >+static struct hda_channel_mode alc882_sixstack_modes[2] = { >+ { 6, alc882_sixstack_ch6_init }, >+ { 8, alc882_sixstack_ch8_init }, >+}; >+ >+/* >+ * macbook pro ALC885 can switch LineIn to LineOut without loosing Mic >+ */ >+ >+/* >+ * 2ch mode >+ */ >+static struct hda_verb alc885_mbp_ch2_init[] = { >+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, >+ { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ { } /* end */ >+}; >+ >+/* >+ * 6ch mode >+ */ >+static struct hda_verb alc885_mbp_ch6_init[] = { >+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, >+ { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ { } /* end */ >+}; >+ >+static struct hda_channel_mode alc885_mbp_6ch_modes[2] = { >+ { 2, alc885_mbp_ch2_init }, >+ { 6, alc885_mbp_ch6_init }, >+}; >+ >+ >+/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17 >+ * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b >+ */ >+static struct snd_kcontrol_new alc882_base_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), >+ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), >+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), >+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc885_mbp3_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT), >+ HDA_BIND_MUTE ("Front Playback Switch", 0x0c, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE ("Speaker Playback Switch", 0x14, 0x00, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Line-Out Playback Volume", 0x0d, 0x00, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE ("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x00, HDA_INPUT), >+ HDA_CODEC_MUTE ("Mic Playback Switch", 0x0b, 0x00, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Boost", 0x1a, 0x00, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x00, HDA_INPUT), >+ { } /* end */ >+}; >+static struct snd_kcontrol_new alc882_w2jc_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), >+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc882_targa_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), >+ { } /* end */ >+}; >+ >+/* Pin assignment: Front=0x14, HP = 0x15, Front = 0x16, ??? >+ * Front Mic=0x18, Line In = 0x1a, Line In = 0x1b, CD = 0x1c >+ */ >+static struct snd_kcontrol_new alc882_asus_a7j_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Mobile Front Playback Switch", 0x16, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mobile Line Playback Volume", 0x0b, 0x03, HDA_INPUT), >+ HDA_CODEC_MUTE("Mobile Line Playback Switch", 0x0b, 0x03, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc882_asus_a7m_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), >+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc882_chmode_mixer[] = { >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Channel Mode", >+ .info = alc_ch_mode_info, >+ .get = alc_ch_mode_get, >+ .put = alc_ch_mode_put, >+ }, >+ { } /* end */ >+}; >+ >+static struct hda_verb alc882_init_verbs[] = { >+ /* Front mixer: unmute input/output amp left and right (volume = 0) */ >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ /* Rear mixer */ >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ /* CLFE mixer */ >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ /* Side mixer */ >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ >+ /* Front Pin: output 0 (0x0c) */ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ /* Rear Pin: output 1 (0x0d) */ >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ /* CLFE Pin: output 2 (0x0e) */ >+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x16, AC_VERB_SET_CONNECT_SEL, 0x02}, >+ /* Side Pin: output 3 (0x0f) */ >+ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, >+ /* Mic (rear) pin: input vref at 80% */ >+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* Front Mic pin: input vref at 80% */ >+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* Line In pin: input */ >+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* Line-2 In: Headphone output (output 0 - 0x0c) */ >+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ /* CD pin widget for input */ >+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ >+ /* FIXME: use matrix-type input source selection */ >+ /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ >+ /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, >+ /* Input mixer2 */ >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, >+ /* Input mixer3 */ >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, >+ /* ADC1: mute amp left and right */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ /* ADC2: mute amp left and right */ >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ /* ADC3: mute amp left and right */ >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ >+ { } >+}; >+ >+static struct hda_verb alc882_eapd_verbs[] = { >+ /* change to EAPD mode */ >+ {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, >+ {0x20, AC_VERB_SET_PROC_COEF, 0x3060}, >+ { } >+}; >+ >+/* Mac Pro test */ >+static struct snd_kcontrol_new alc882_macpro_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x18, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT), >+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x02, HDA_INPUT), >+ { } /* end */ >+}; >+ >+static struct hda_verb alc882_macpro_init_verbs[] = { >+ /* Front mixer: unmute input/output amp left and right (volume = 0) */ >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ /* Front Pin: output 0 (0x0c) */ >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ /* Front Mic pin: input vref at 80% */ >+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* Speaker: output */ >+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x1a, AC_VERB_SET_CONNECT_SEL, 0x04}, >+ /* Headphone output (output 0 - 0x0c) */ >+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x18, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ >+ /* FIXME: use matrix-type input source selection */ >+ /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ >+ /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, >+ /* Input mixer2 */ >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, >+ /* Input mixer3 */ >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, >+ /* ADC1: mute amp left and right */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ /* ADC2: mute amp left and right */ >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ /* ADC3: mute amp left and right */ >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ >+ { } >+}; >+ >+/* Macbook Pro rev3 */ >+static struct hda_verb alc885_mbp3_init_verbs[] = { >+ /* Front mixer: unmute input/output amp left and right (volume = 0) */ >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ /* Rear mixer */ >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ /* Front Pin: output 0 (0x0c) */ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ /* HP Pin: output 0 (0x0d) */ >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc4}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, >+ /* Mic (rear) pin: input vref at 80% */ >+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* Front Mic pin: input vref at 80% */ >+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* Line In pin: use output 1 when in LineOut mode */ >+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ >+ /* FIXME: use matrix-type input source selection */ >+ /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ >+ /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, >+ /* Input mixer2 */ >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, >+ /* Input mixer3 */ >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, >+ /* ADC1: mute amp left and right */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ /* ADC2: mute amp left and right */ >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ /* ADC3: mute amp left and right */ >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ >+ { } >+}; >+ >+/* iMac 24 mixer. */ >+static struct snd_kcontrol_new alc885_imac24_mixer[] = { >+ HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x00, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Master Playback Switch", 0x0c, 0x00, HDA_INPUT), >+ { } /* end */ >+}; >+ >+/* iMac 24 init verbs. */ >+static struct hda_verb alc885_imac24_init_verbs[] = { >+ /* Internal speakers: output 0 (0x0c) */ >+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x18, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ /* Internal speakers: output 0 (0x0c) */ >+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ /* Headphone: output 0 (0x0c) */ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, >+ /* Front Mic: input vref at 80% */ >+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ { } >+}; >+ >+/* Toggle speaker-output according to the hp-jack state */ >+static void alc885_imac24_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ >+ present = snd_hda_codec_read(codec, 0x14, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ snd_hda_codec_amp_stereo(codec, 0x18, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); >+ snd_hda_codec_amp_stereo(codec, 0x1a, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); >+} >+ >+/* Processes unsolicited events. */ >+static void alc885_imac24_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ /* Headphone insertion or removal. */ >+ if ((res >> 26) == ALC880_HP_EVENT) >+ alc885_imac24_automute(codec); >+} >+ >+static void alc885_mbp3_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ >+ present = snd_hda_codec_read(codec, 0x15, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); >+ snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, present ? 0 : HDA_AMP_MUTE); >+ >+} >+static void alc885_mbp3_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ /* Headphone insertion or removal. */ >+ if ((res >> 26) == ALC880_HP_EVENT) >+ alc885_mbp3_automute(codec); >+} >+ >+ >+static struct hda_verb alc882_targa_verbs[] = { >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ >+ {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */ >+ {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */ >+ {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ >+ >+ {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, >+ {0x01, AC_VERB_SET_GPIO_MASK, 0x03}, >+ {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x03}, >+ {0x01, AC_VERB_SET_GPIO_DATA, 0x03}, >+ { } /* end */ >+}; >+ >+/* toggle speaker-output according to the hp-jack state */ >+static void alc882_targa_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ >+ present = snd_hda_codec_read(codec, 0x14, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); >+ snd_hda_codec_write_cache(codec, 1, 0, AC_VERB_SET_GPIO_DATA, >+ present ? 1 : 3); >+} >+ >+static void alc882_targa_unsol_event(struct hda_codec *codec, unsigned int res) >+{ >+ /* Looks like the unsol event is incompatible with the standard >+ * definition. 4bit tag is placed at 26 bit! >+ */ >+ if (((res >> 26) == ALC880_HP_EVENT)) { >+ alc882_targa_automute(codec); >+ } >+} >+ >+static struct hda_verb alc882_asus_a7j_verbs[] = { >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ >+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */ >+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ >+ {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */ >+ >+ {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */ >+ {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */ >+ {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ >+ { } /* end */ >+}; >+ >+static struct hda_verb alc882_asus_a7m_verbs[] = { >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ >+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */ >+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ >+ {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */ >+ >+ {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */ >+ {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */ >+ {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ >+ { } /* end */ >+}; >+ >+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_macpro_init_hook(struct hda_codec *codec) >+{ >+ alc882_gpio_mute(codec, 0, 0); >+ alc882_gpio_mute(codec, 1, 0); >+} >+ >+/* set up GPIO and update auto-muting at initialization */ >+static void alc885_imac24_init_hook(struct hda_codec *codec) >+{ >+ alc885_macpro_init_hook(codec); >+ alc885_imac24_automute(codec); >+} >+ >+/* >+ * generic initialization of ADC, input mixers and output mixers >+ */ >+static struct hda_verb alc882_auto_init_verbs[] = { >+ /* >+ * Unmute ADC0-2 and set the default input to mic-in >+ */ >+ {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ >+ /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback >+ * mixer widget >+ * Note: PASD motherboards uses the Line In 2 as the input for >+ * front panel mic (mic 2) >+ */ >+ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, >+ >+ /* >+ * Set up output mixers (0x0c - 0x0f) >+ */ >+ /* set vol=0 to output mixers */ >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ /* set up input amps for analog loopback */ >+ /* Amp Indices: DAC = 0, mixer = 1 */ >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ >+ /* FIXME: use matrix-type input source selection */ >+ /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ >+ /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, >+ /* Input mixer2 */ >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, >+ /* Input mixer3 */ >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, >+ >+ { } >+}; >+ >+/* capture mixer elements */ >+static struct snd_kcontrol_new alc882_capture_alt_mixer[] = { >+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ /* The multiple "Capture Source" controls confuse alsamixer >+ * So call somewhat different.. >+ */ >+ /* .name = "Capture Source", */ >+ .name = "Input Source", >+ .count = 2, >+ .info = alc882_mux_enum_info, >+ .get = alc882_mux_enum_get, >+ .put = alc882_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc882_capture_mixer[] = { >+ HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ /* The multiple "Capture Source" controls confuse alsamixer >+ * So call somewhat different.. >+ */ >+ /* .name = "Capture Source", */ >+ .name = "Input Source", >+ .count = 3, >+ .info = alc882_mux_enum_info, >+ .get = alc882_mux_enum_get, >+ .put = alc882_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+#define alc882_loopbacks alc880_loopbacks >+#endif >+ >+/* pcm configuration: identiacal with ALC880 */ >+#define alc882_pcm_analog_playback alc880_pcm_analog_playback >+#define alc882_pcm_analog_capture alc880_pcm_analog_capture >+#define alc882_pcm_digital_playback alc880_pcm_digital_playback >+#define alc882_pcm_digital_capture alc880_pcm_digital_capture >+ >+/* >+ * configuration and preset >+ */ >+static const char *alc882_models[ALC882_MODEL_LAST] = { >+ [ALC882_3ST_DIG] = "3stack-dig", >+ [ALC882_6ST_DIG] = "6stack-dig", >+ [ALC882_ARIMA] = "arima", >+ [ALC882_W2JC] = "w2jc", >+ [ALC882_TARGA] = "targa", >+ [ALC882_ASUS_A7J] = "asus-a7j", >+ [ALC882_ASUS_A7M] = "asus-a7m", >+ [ALC885_MACPRO] = "macpro", >+ [ALC885_MBP3] = "mbp3", >+ [ALC885_IMAC24] = "imac24", >+ [ALC882_AUTO] = "auto", >+}; >+ >+static struct snd_pci_quirk alc882_cfg_tbl[] = { >+ SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC882_6ST_DIG), >+ SND_PCI_QUIRK(0x1043, 0x060d, "Asus A7J", ALC882_ASUS_A7J), >+ SND_PCI_QUIRK(0x1043, 0x1243, "Asus A7J", ALC882_ASUS_A7J), >+ SND_PCI_QUIRK(0x1043, 0x13c2, "Asus A7M", ALC882_ASUS_A7M), >+ SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_W2JC), >+ SND_PCI_QUIRK(0x1043, 0x817f, "Asus P5LD2", ALC882_6ST_DIG), >+ SND_PCI_QUIRK(0x1043, 0x81d8, "Asus P5WD", ALC882_6ST_DIG), >+ SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC882_6ST_DIG), >+ SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte P35 DS3R", ALC882_6ST_DIG), >+ SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8 */ >+ SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG), >+ SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA), >+ {} >+}; >+ >+static struct alc_config_preset alc882_presets[] = { >+ [ALC882_3ST_DIG] = { >+ .mixers = { alc882_base_mixer }, >+ .init_verbs = { alc882_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc882_dac_nids), >+ .dac_nids = alc882_dac_nids, >+ .dig_out_nid = ALC882_DIGOUT_NID, >+ .dig_in_nid = ALC882_DIGIN_NID, >+ .num_channel_mode = ARRAY_SIZE(alc882_ch_modes), >+ .channel_mode = alc882_ch_modes, >+ .need_dac_fix = 1, >+ .input_mux = &alc882_capture_source, >+ }, >+ [ALC882_6ST_DIG] = { >+ .mixers = { alc882_base_mixer, alc882_chmode_mixer }, >+ .init_verbs = { alc882_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc882_dac_nids), >+ .dac_nids = alc882_dac_nids, >+ .dig_out_nid = ALC882_DIGOUT_NID, >+ .dig_in_nid = ALC882_DIGIN_NID, >+ .num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes), >+ .channel_mode = alc882_sixstack_modes, >+ .input_mux = &alc882_capture_source, >+ }, >+ [ALC882_ARIMA] = { >+ .mixers = { alc882_base_mixer, alc882_chmode_mixer }, >+ .init_verbs = { alc882_init_verbs, alc882_eapd_verbs }, >+ .num_dacs = ARRAY_SIZE(alc882_dac_nids), >+ .dac_nids = alc882_dac_nids, >+ .num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes), >+ .channel_mode = alc882_sixstack_modes, >+ .input_mux = &alc882_capture_source, >+ }, >+ [ALC882_W2JC] = { >+ .mixers = { alc882_w2jc_mixer, alc882_chmode_mixer }, >+ .init_verbs = { alc882_init_verbs, alc882_eapd_verbs, >+ alc880_gpio1_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc882_dac_nids), >+ .dac_nids = alc882_dac_nids, >+ .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), >+ .channel_mode = alc880_threestack_modes, >+ .need_dac_fix = 1, >+ .input_mux = &alc882_capture_source, >+ .dig_out_nid = ALC882_DIGOUT_NID, >+ }, >+ [ALC885_MBP3] = { >+ .mixers = { alc885_mbp3_mixer, alc882_chmode_mixer }, >+ .init_verbs = { alc885_mbp3_init_verbs, >+ alc880_gpio1_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc882_dac_nids), >+ .dac_nids = alc882_dac_nids, >+ .channel_mode = alc885_mbp_6ch_modes, >+ .num_channel_mode = ARRAY_SIZE(alc885_mbp_6ch_modes), >+ .input_mux = &alc882_capture_source, >+ .dig_out_nid = ALC882_DIGOUT_NID, >+ .dig_in_nid = ALC882_DIGIN_NID, >+ .unsol_event = alc885_mbp3_unsol_event, >+ .init_hook = alc885_mbp3_automute, >+ }, >+ [ALC885_MACPRO] = { >+ .mixers = { alc882_macpro_mixer }, >+ .init_verbs = { alc882_macpro_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc882_dac_nids), >+ .dac_nids = alc882_dac_nids, >+ .dig_out_nid = ALC882_DIGOUT_NID, >+ .dig_in_nid = ALC882_DIGIN_NID, >+ .num_channel_mode = ARRAY_SIZE(alc882_ch_modes), >+ .channel_mode = alc882_ch_modes, >+ .input_mux = &alc882_capture_source, >+ .init_hook = alc885_macpro_init_hook, >+ }, >+ [ALC885_IMAC24] = { >+ .mixers = { alc885_imac24_mixer }, >+ .init_verbs = { alc885_imac24_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc882_dac_nids), >+ .dac_nids = alc882_dac_nids, >+ .dig_out_nid = ALC882_DIGOUT_NID, >+ .dig_in_nid = ALC882_DIGIN_NID, >+ .num_channel_mode = ARRAY_SIZE(alc882_ch_modes), >+ .channel_mode = alc882_ch_modes, >+ .input_mux = &alc882_capture_source, >+ .unsol_event = alc885_imac24_unsol_event, >+ .init_hook = alc885_imac24_init_hook, >+ }, >+ [ALC882_TARGA] = { >+ .mixers = { alc882_targa_mixer, alc882_chmode_mixer, >+ alc882_capture_mixer }, >+ .init_verbs = { alc882_init_verbs, alc882_targa_verbs}, >+ .num_dacs = ARRAY_SIZE(alc882_dac_nids), >+ .dac_nids = alc882_dac_nids, >+ .dig_out_nid = ALC882_DIGOUT_NID, >+ .num_adc_nids = ARRAY_SIZE(alc882_adc_nids), >+ .adc_nids = alc882_adc_nids, >+ .capsrc_nids = alc882_capsrc_nids, >+ .num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes), >+ .channel_mode = alc882_3ST_6ch_modes, >+ .need_dac_fix = 1, >+ .input_mux = &alc882_capture_source, >+ .unsol_event = alc882_targa_unsol_event, >+ .init_hook = alc882_targa_automute, >+ }, >+ [ALC882_ASUS_A7J] = { >+ .mixers = { alc882_asus_a7j_mixer, alc882_chmode_mixer, >+ alc882_capture_mixer }, >+ .init_verbs = { alc882_init_verbs, alc882_asus_a7j_verbs}, >+ .num_dacs = ARRAY_SIZE(alc882_dac_nids), >+ .dac_nids = alc882_dac_nids, >+ .dig_out_nid = ALC882_DIGOUT_NID, >+ .num_adc_nids = ARRAY_SIZE(alc882_adc_nids), >+ .adc_nids = alc882_adc_nids, >+ .capsrc_nids = alc882_capsrc_nids, >+ .num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes), >+ .channel_mode = alc882_3ST_6ch_modes, >+ .need_dac_fix = 1, >+ .input_mux = &alc882_capture_source, >+ }, >+ [ALC882_ASUS_A7M] = { >+ .mixers = { alc882_asus_a7m_mixer, alc882_chmode_mixer }, >+ .init_verbs = { alc882_init_verbs, alc882_eapd_verbs, >+ alc880_gpio1_init_verbs, >+ alc882_asus_a7m_verbs }, >+ .num_dacs = ARRAY_SIZE(alc882_dac_nids), >+ .dac_nids = alc882_dac_nids, >+ .dig_out_nid = ALC882_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), >+ .channel_mode = alc880_threestack_modes, >+ .need_dac_fix = 1, >+ .input_mux = &alc882_capture_source, >+ }, >+}; >+ >+ >+/* >+ * Pin config fixes >+ */ >+enum { >+ PINFIX_ABIT_AW9D_MAX >+}; >+ >+static struct alc_pincfg alc882_abit_aw9d_pinfix[] = { >+ { 0x15, 0x01080104 }, /* side */ >+ { 0x16, 0x01011012 }, /* rear */ >+ { 0x17, 0x01016011 }, /* clfe */ >+ { } >+}; >+ >+static const struct alc_pincfg *alc882_pin_fixes[] = { >+ [PINFIX_ABIT_AW9D_MAX] = alc882_abit_aw9d_pinfix, >+}; >+ >+static struct snd_pci_quirk alc882_pinfix_tbl[] = { >+ SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX), >+ {} >+}; >+ >+/* >+ * BIOS auto configuration >+ */ >+static void alc882_auto_set_output_and_unmute(struct hda_codec *codec, >+ hda_nid_t nid, int pin_type, >+ int dac_idx) >+{ >+ /* set as output */ >+ struct alc_spec *spec = codec->spec; >+ int idx; >+ >+ alc_set_pin_output(codec, nid, pin_type); >+ if (spec->multiout.dac_nids[dac_idx] == 0x25) >+ idx = 4; >+ else >+ idx = spec->multiout.dac_nids[dac_idx] - 2; >+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx); >+ >+} >+ >+static void alc882_auto_init_multi_out(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ int i; >+ >+ alc_subsystem_id(codec, 0x15, 0x1b, 0x14); >+ for (i = 0; i <= HDA_SIDE; i++) { >+ hda_nid_t nid = spec->autocfg.line_out_pins[i]; >+ int pin_type = get_pin_type(spec->autocfg.line_out_type); >+ if (nid) >+ alc882_auto_set_output_and_unmute(codec, nid, pin_type, >+ i); >+ } >+} >+ >+static void alc882_auto_init_hp_out(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ hda_nid_t pin; >+ >+ pin = spec->autocfg.hp_pins[0]; >+ if (pin) /* connect to front */ >+ /* use dac 0 */ >+ alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); >+ pin = spec->autocfg.speaker_pins[0]; >+ if (pin) >+ alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); >+} >+ >+#define alc882_is_input_pin(nid) alc880_is_input_pin(nid) >+#define ALC882_PIN_CD_NID ALC880_PIN_CD_NID >+ >+static void alc882_auto_init_analog_input(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ int i; >+ >+ for (i = 0; i < AUTO_PIN_LAST; i++) { >+ hda_nid_t nid = spec->autocfg.input_pins[i]; >+ unsigned int vref; >+ if (!nid) >+ continue; >+ vref = PIN_IN; >+ if (1 /*i <= AUTO_PIN_FRONT_MIC*/) { >+ unsigned int pincap; >+ pincap = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); >+ if ((pincap >> AC_PINCAP_VREF_SHIFT) & >+ AC_PINCAP_VREF_80) >+ vref = PIN_VREF80; >+ } >+ snd_hda_codec_write(codec, nid, 0, >+ AC_VERB_SET_PIN_WIDGET_CONTROL, vref); >+ if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) >+ snd_hda_codec_write(codec, nid, 0, >+ AC_VERB_SET_AMP_GAIN_MUTE, >+ AMP_OUT_MUTE); >+ } >+} >+ >+/* add mic boosts if needed */ >+static int alc_auto_add_mic_boost(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ int err; >+ hda_nid_t nid; >+ >+ nid = spec->autocfg.input_pins[AUTO_PIN_MIC]; >+ if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) { >+ err = add_control(spec, ALC_CTL_WIDGET_VOL, >+ "Mic Boost", >+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT)); >+ if (err < 0) >+ return err; >+ } >+ nid = spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC]; >+ if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) { >+ err = add_control(spec, ALC_CTL_WIDGET_VOL, >+ "Front Mic Boost", >+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT)); >+ if (err < 0) >+ return err; >+ } >+ return 0; >+} >+ >+/* almost identical with ALC880 parser... */ >+static int alc882_parse_auto_config(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ int err = alc880_parse_auto_config(codec); >+ >+ if (err < 0) >+ return err; >+ else if (!err) >+ return 0; /* no config found */ >+ >+ err = alc_auto_add_mic_boost(codec); >+ if (err < 0) >+ return err; >+ >+ /* hack - override the init verbs */ >+ spec->init_verbs[0] = alc882_auto_init_verbs; >+ >+ return 1; /* config found */ >+} >+ >+/* additional initialization for auto-configuration model */ >+static void alc882_auto_init(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ alc882_auto_init_multi_out(codec); >+ alc882_auto_init_hp_out(codec); >+ alc882_auto_init_analog_input(codec); >+ if (spec->unsol_event) >+ alc_sku_automute(codec); >+} >+ >+static int patch_alc883(struct hda_codec *codec); /* called in patch_alc882() */ >+ >+static int patch_alc882(struct hda_codec *codec) >+{ >+ struct alc_spec *spec; >+ int err, board_config; >+ >+ spec = kzalloc(sizeof(*spec), GFP_KERNEL); >+ if (spec == NULL) >+ return -ENOMEM; >+ >+ codec->spec = spec; >+ >+ board_config = snd_hda_check_board_config(codec, ALC882_MODEL_LAST, >+ alc882_models, >+ alc882_cfg_tbl); >+ >+ if (board_config < 0 || board_config >= ALC882_MODEL_LAST) { >+ /* Pick up systems that don't supply PCI SSID */ >+ switch (codec->subsystem_id) { >+ case 0x106b0c00: /* Mac Pro */ >+ board_config = ALC885_MACPRO; >+ break; >+ case 0x106b1000: /* iMac 24 */ >+ board_config = ALC885_IMAC24; >+ break; >+ case 0x106b00a1: /* Macbook */ >+ case 0x106b2c00: /* Macbook Pro rev3 */ >+ board_config = ALC885_MBP3; >+ break; >+ default: >+ /* ALC889A is handled better as ALC888-compatible */ >+ if (codec->revision_id == 0x100103) { >+ alc_free(codec); >+ return patch_alc883(codec); >+ } >+ printk(KERN_INFO "hda_codec: Unknown model for ALC882, " >+ "trying auto-probe from BIOS...\n"); >+ board_config = ALC882_AUTO; >+ } >+ } >+ >+ alc_fix_pincfg(codec, alc882_pinfix_tbl, alc882_pin_fixes); >+ >+ if (board_config == ALC882_AUTO) { >+ /* automatic parse from the BIOS config */ >+ err = alc882_parse_auto_config(codec); >+ if (err < 0) { >+ alc_free(codec); >+ return err; >+ } else if (!err) { >+ printk(KERN_INFO >+ "hda_codec: Cannot set up configuration " >+ "from BIOS. Using base mode...\n"); >+ board_config = ALC882_3ST_DIG; >+ } >+ } >+ >+ if (board_config != ALC882_AUTO) >+ setup_preset(spec, &alc882_presets[board_config]); >+ >+ if (codec->vendor_id == 0x10ec0885) { >+ spec->stream_name_analog = "ALC885 Analog"; >+ spec->stream_name_digital = "ALC885 Digital"; >+ } else { >+ spec->stream_name_analog = "ALC882 Analog"; >+ spec->stream_name_digital = "ALC882 Digital"; >+ } >+ >+ spec->stream_analog_playback = &alc882_pcm_analog_playback; >+ spec->stream_analog_capture = &alc882_pcm_analog_capture; >+ /* FIXME: setup DAC5 */ >+ /*spec->stream_analog_alt_playback = &alc880_pcm_analog_alt_playback;*/ >+ spec->stream_analog_alt_capture = &alc880_pcm_analog_alt_capture; >+ >+ spec->stream_digital_playback = &alc882_pcm_digital_playback; >+ spec->stream_digital_capture = &alc882_pcm_digital_capture; >+ >+ if (!spec->adc_nids && spec->input_mux) { >+ /* check whether NID 0x07 is valid */ >+ unsigned int wcap = get_wcaps(codec, 0x07); >+ /* get type */ >+ wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; >+ if (wcap != AC_WID_AUD_IN) { >+ spec->adc_nids = alc882_adc_nids_alt; >+ spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids_alt); >+ spec->capsrc_nids = alc882_capsrc_nids_alt; >+ spec->mixers[spec->num_mixers] = >+ alc882_capture_alt_mixer; >+ spec->num_mixers++; >+ } else { >+ spec->adc_nids = alc882_adc_nids; >+ spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids); >+ spec->capsrc_nids = alc882_capsrc_nids; >+ spec->mixers[spec->num_mixers] = alc882_capture_mixer; >+ spec->num_mixers++; >+ } >+ } >+ >+ spec->vmaster_nid = 0x0c; >+ >+ codec->patch_ops = alc_patch_ops; >+ if (board_config == ALC882_AUTO) >+ spec->init_hook = alc882_auto_init; >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+ if (!spec->loopback.amplist) >+ spec->loopback.amplist = alc882_loopbacks; >+#endif >+ >+ return 0; >+} >+ >+/* >+ * ALC883 support >+ * >+ * ALC883 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). >+ */ >+#define ALC883_DIGOUT_NID 0x06 >+#define ALC883_DIGIN_NID 0x0a >+ >+static hda_nid_t alc883_dac_nids[4] = { >+ /* front, rear, clfe, rear_surr */ >+ 0x02, 0x03, 0x04, 0x05 >+}; >+ >+static hda_nid_t alc883_adc_nids[2] = { >+ /* ADC1-2 */ >+ 0x08, 0x09, >+}; >+ >+static hda_nid_t alc883_capsrc_nids[2] = { 0x23, 0x22 }; >+ >+/* input MUX */ >+/* FIXME: should be a matrix-type input source selection */ >+ >+static struct hda_input_mux alc883_capture_source = { >+ .num_items = 4, >+ .items = { >+ { "Mic", 0x0 }, >+ { "Front Mic", 0x1 }, >+ { "Line", 0x2 }, >+ { "CD", 0x4 }, >+ }, >+}; >+ >+static struct hda_input_mux alc883_3stack_6ch_intel = { >+ .num_items = 4, >+ .items = { >+ { "Mic", 0x1 }, >+ { "Front Mic", 0x0 }, >+ { "Line", 0x2 }, >+ { "CD", 0x4 }, >+ }, >+}; >+ >+static struct hda_input_mux alc883_lenovo_101e_capture_source = { >+ .num_items = 2, >+ .items = { >+ { "Mic", 0x1 }, >+ { "Line", 0x2 }, >+ }, >+}; >+ >+static struct hda_input_mux alc883_lenovo_nb0763_capture_source = { >+ .num_items = 4, >+ .items = { >+ { "Mic", 0x0 }, >+ { "iMic", 0x1 }, >+ { "Line", 0x2 }, >+ { "CD", 0x4 }, >+ }, >+}; >+ >+static struct hda_input_mux alc883_fujitsu_pi2515_capture_source = { >+ .num_items = 2, >+ .items = { >+ { "Mic", 0x0 }, >+ { "Int Mic", 0x1 }, >+ }, >+}; >+ >+#define alc883_mux_enum_info alc_mux_enum_info >+#define alc883_mux_enum_get alc_mux_enum_get >+/* ALC883 has the ALC882-type input selection */ >+#define alc883_mux_enum_put alc882_mux_enum_put >+ >+/* >+ * 2ch mode >+ */ >+static struct hda_channel_mode alc883_3ST_2ch_modes[1] = { >+ { 2, NULL } >+}; >+ >+/* >+ * 2ch mode >+ */ >+static struct hda_verb alc883_3ST_ch2_init[] = { >+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, >+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, >+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, >+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, >+ { } /* end */ >+}; >+ >+/* >+ * 4ch mode >+ */ >+static struct hda_verb alc883_3ST_ch4_init[] = { >+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, >+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, >+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, >+ { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, >+ { } /* end */ >+}; >+ >+/* >+ * 6ch mode >+ */ >+static struct hda_verb alc883_3ST_ch6_init[] = { >+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, >+ { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, >+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, >+ { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, >+ { } /* end */ >+}; >+ >+static struct hda_channel_mode alc883_3ST_6ch_modes[3] = { >+ { 2, alc883_3ST_ch2_init }, >+ { 4, alc883_3ST_ch4_init }, >+ { 6, alc883_3ST_ch6_init }, >+}; >+ >+/* >+ * 2ch mode >+ */ >+static struct hda_verb alc883_3ST_ch2_intel_init[] = { >+ { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, >+ { 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, >+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, >+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, >+ { } /* end */ >+}; >+ >+/* >+ * 4ch mode >+ */ >+static struct hda_verb alc883_3ST_ch4_intel_init[] = { >+ { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, >+ { 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, >+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, >+ { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, >+ { } /* end */ >+}; >+ >+/* >+ * 6ch mode >+ */ >+static struct hda_verb alc883_3ST_ch6_intel_init[] = { >+ { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, >+ { 0x19, AC_VERB_SET_CONNECT_SEL, 0x02 }, >+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, >+ { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, >+ { } /* end */ >+}; >+ >+static struct hda_channel_mode alc883_3ST_6ch_intel_modes[3] = { >+ { 2, alc883_3ST_ch2_intel_init }, >+ { 4, alc883_3ST_ch4_intel_init }, >+ { 6, alc883_3ST_ch6_intel_init }, >+}; >+ >+/* >+ * 6ch mode >+ */ >+static struct hda_verb alc883_sixstack_ch6_init[] = { >+ { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, >+ { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { } /* end */ >+}; >+ >+/* >+ * 8ch mode >+ */ >+static struct hda_verb alc883_sixstack_ch8_init[] = { >+ { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { } /* end */ >+}; >+ >+static struct hda_channel_mode alc883_sixstack_modes[2] = { >+ { 6, alc883_sixstack_ch6_init }, >+ { 8, alc883_sixstack_ch8_init }, >+}; >+ >+static struct hda_verb alc883_medion_eapd_verbs[] = { >+ /* eanable EAPD on medion laptop */ >+ {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, >+ {0x20, AC_VERB_SET_PROC_COEF, 0x3070}, >+ { } >+}; >+ >+/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17 >+ * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b >+ */ >+ >+static struct snd_kcontrol_new alc883_base_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), >+ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), >+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), >+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), >+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ /* .name = "Capture Source", */ >+ .name = "Input Source", >+ .count = 2, >+ .info = alc883_mux_enum_info, >+ .get = alc883_mux_enum_get, >+ .put = alc883_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc883_mitac_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), >+ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), >+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ /* .name = "Capture Source", */ >+ .name = "Input Source", >+ .count = 2, >+ .info = alc883_mux_enum_info, >+ .get = alc883_mux_enum_get, >+ .put = alc883_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc883_clevo_m720_mixer[] = { >+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT), >+ HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ /* .name = "Capture Source", */ >+ .name = "Input Source", >+ .count = 2, >+ .info = alc883_mux_enum_info, >+ .get = alc883_mux_enum_get, >+ .put = alc883_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc883_2ch_fujitsu_pi2515_mixer[] = { >+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT), >+ HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ /* .name = "Capture Source", */ >+ .name = "Input Source", >+ .count = 2, >+ .info = alc883_mux_enum_info, >+ .get = alc883_mux_enum_get, >+ .put = alc883_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc883_3ST_2ch_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), >+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), >+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), >+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ /* .name = "Capture Source", */ >+ .name = "Input Source", >+ .count = 2, >+ .info = alc883_mux_enum_info, >+ .get = alc883_mux_enum_get, >+ .put = alc883_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc883_3ST_6ch_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), >+ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), >+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), >+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), >+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ /* .name = "Capture Source", */ >+ .name = "Input Source", >+ .count = 2, >+ .info = alc883_mux_enum_info, >+ .get = alc883_mux_enum_get, >+ .put = alc883_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc883_3ST_6ch_intel_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, >+ HDA_OUTPUT), >+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), >+ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x19, 0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Boost", 0x18, 0, HDA_INPUT), >+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), >+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), >+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ /* .name = "Capture Source", */ >+ .name = "Input Source", >+ .count = 2, >+ .info = alc883_mux_enum_info, >+ .get = alc883_mux_enum_get, >+ .put = alc883_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc883_fivestack_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x16, 1, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), >+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), >+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), >+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), >+ >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ /* .name = "Capture Source", */ >+ .name = "Input Source", >+ .count = 1, >+ .info = alc883_mux_enum_info, >+ .get = alc883_mux_enum_get, >+ .put = alc883_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc883_tagra_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), >+ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ /* .name = "Capture Source", */ >+ .name = "Input Source", >+ .count = 2, >+ .info = alc883_mux_enum_info, >+ .get = alc883_mux_enum_get, >+ .put = alc883_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc883_tagra_2ch_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT), >+ HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ /* .name = "Capture Source", */ >+ .name = "Input Source", >+ .count = 2, >+ .info = alc883_mux_enum_info, >+ .get = alc883_mux_enum_get, >+ .put = alc883_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc883_lenovo_101e_2ch_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ /* .name = "Capture Source", */ >+ .name = "Input Source", >+ .count = 1, >+ .info = alc883_mux_enum_info, >+ .get = alc883_mux_enum_get, >+ .put = alc883_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc883_lenovo_nb0763_mixer[] = { >+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 2, HDA_INPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("iMic Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_MUTE("iMic Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ /* .name = "Capture Source", */ >+ .name = "Input Source", >+ .count = 2, >+ .info = alc883_mux_enum_info, >+ .get = alc883_mux_enum_get, >+ .put = alc883_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc883_medion_md2_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ /* .name = "Capture Source", */ >+ .name = "Input Source", >+ .count = 2, >+ .info = alc883_mux_enum_info, >+ .get = alc883_mux_enum_get, >+ .put = alc883_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc883_acer_aspire_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ /* .name = "Capture Source", */ >+ .name = "Input Source", >+ .count = 2, >+ .info = alc883_mux_enum_info, >+ .get = alc883_mux_enum_get, >+ .put = alc883_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc883_chmode_mixer[] = { >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Channel Mode", >+ .info = alc_ch_mode_info, >+ .get = alc_ch_mode_get, >+ .put = alc_ch_mode_put, >+ }, >+ { } /* end */ >+}; >+ >+static struct hda_verb alc883_init_verbs[] = { >+ /* ADC1: mute amp left and right */ >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ /* ADC2: mute amp left and right */ >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ /* Front mixer: unmute input/output amp left and right (volume = 0) */ >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ /* Rear mixer */ >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ /* CLFE mixer */ >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ /* Side mixer */ >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ >+ /* mute analog input loopbacks */ >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, >+ >+ /* Front Pin: output 0 (0x0c) */ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ /* Rear Pin: output 1 (0x0d) */ >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ /* CLFE Pin: output 2 (0x0e) */ >+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x16, AC_VERB_SET_CONNECT_SEL, 0x02}, >+ /* Side Pin: output 3 (0x0f) */ >+ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, >+ /* Mic (rear) pin: input vref at 80% */ >+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* Front Mic pin: input vref at 80% */ >+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* Line In pin: input */ >+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* Line-2 In: Headphone output (output 0 - 0x0c) */ >+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ /* CD pin widget for input */ >+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ >+ /* FIXME: use matrix-type input source selection */ >+ /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ >+ /* Input mixer2 */ >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, >+ /* Input mixer3 */ >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, >+ { } >+}; >+ >+/* toggle speaker-output according to the hp-jack state */ >+static void alc883_mitac_hp_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ >+ present = snd_hda_codec_read(codec, 0x15, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); >+ snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); >+} >+ >+/* auto-toggle front mic */ >+/* >+static void alc883_mitac_mic_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ unsigned char bits; >+ >+ present = snd_hda_codec_read(codec, 0x18, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ bits = present ? HDA_AMP_MUTE : 0; >+ snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits); >+} >+*/ >+ >+static void alc883_mitac_automute(struct hda_codec *codec) >+{ >+ alc883_mitac_hp_automute(codec); >+ /* alc883_mitac_mic_automute(codec); */ >+} >+ >+static void alc883_mitac_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ switch (res >> 26) { >+ case ALC880_HP_EVENT: >+ alc883_mitac_hp_automute(codec); >+ break; >+ case ALC880_MIC_EVENT: >+ /* alc883_mitac_mic_automute(codec); */ >+ break; >+ } >+} >+ >+static struct hda_verb alc883_mitac_verbs[] = { >+ /* HP */ >+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ /* Subwoofer */ >+ {0x17, AC_VERB_SET_CONNECT_SEL, 0x02}, >+ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ >+ /* enable unsolicited event */ >+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, >+ /* {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, */ >+ >+ { } /* end */ >+}; >+ >+static struct hda_verb alc883_clevo_m720_verbs[] = { >+ /* HP */ >+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ /* Int speaker */ >+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ >+ /* enable unsolicited event */ >+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, >+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, >+ >+ { } /* end */ >+}; >+ >+static struct hda_verb alc883_2ch_fujitsu_pi2515_verbs[] = { >+ /* HP */ >+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ /* Subwoofer */ >+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ >+ /* enable unsolicited event */ >+ {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, >+ >+ { } /* end */ >+}; >+ >+static struct hda_verb alc883_tagra_verbs[] = { >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ >+ {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */ >+ {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */ >+ {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ >+ >+ {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, >+ {0x01, AC_VERB_SET_GPIO_MASK, 0x03}, >+ {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x03}, >+ {0x01, AC_VERB_SET_GPIO_DATA, 0x03}, >+ >+ { } /* end */ >+}; >+ >+static struct hda_verb alc883_lenovo_101e_verbs[] = { >+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_FRONT_EVENT|AC_USRSP_EN}, >+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT|AC_USRSP_EN}, >+ { } /* end */ >+}; >+ >+static struct hda_verb alc883_lenovo_nb0763_verbs[] = { >+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ { } /* end */ >+}; >+ >+static struct hda_verb alc888_lenovo_ms7195_verbs[] = { >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_FRONT_EVENT | AC_USRSP_EN}, >+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, >+ { } /* end */ >+}; >+ >+static struct hda_verb alc883_haier_w66_verbs[] = { >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ >+ {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, >+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ { } /* end */ >+}; >+ >+static struct hda_verb alc888_3st_hp_verbs[] = { >+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front: output 0 (0x0c) */ >+ {0x16, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Rear : output 1 (0x0d) */ >+ {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* CLFE : output 2 (0x0e) */ >+ { } >+}; >+ >+static struct hda_verb alc888_6st_dell_verbs[] = { >+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, >+ { } >+}; >+ >+static struct hda_verb alc888_3st_hp_2ch_init[] = { >+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, >+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, >+ { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, >+ { 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, >+ { } >+}; >+ >+static struct hda_verb alc888_3st_hp_6ch_init[] = { >+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, >+ { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, >+ { } >+}; >+ >+static struct hda_channel_mode alc888_3st_hp_modes[2] = { >+ { 2, alc888_3st_hp_2ch_init }, >+ { 6, alc888_3st_hp_6ch_init }, >+}; >+ >+/* toggle front-jack and RCA according to the hp-jack state */ >+static void alc888_lenovo_ms7195_front_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ >+ present = snd_hda_codec_read(codec, 0x1b, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); >+ snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); >+} >+ >+/* toggle RCA according to the front-jack state */ >+static void alc888_lenovo_ms7195_rca_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ >+ present = snd_hda_codec_read(codec, 0x14, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); >+} >+ >+static void alc883_lenovo_ms7195_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ if ((res >> 26) == ALC880_HP_EVENT) >+ alc888_lenovo_ms7195_front_automute(codec); >+ if ((res >> 26) == ALC880_FRONT_EVENT) >+ alc888_lenovo_ms7195_rca_automute(codec); >+} >+ >+static struct hda_verb alc883_medion_md2_verbs[] = { >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ >+ {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, >+ { } /* end */ >+}; >+ >+/* toggle speaker-output according to the hp-jack state */ >+static void alc883_medion_md2_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ >+ present = snd_hda_codec_read(codec, 0x14, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); >+} >+ >+static void alc883_medion_md2_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ if ((res >> 26) == ALC880_HP_EVENT) >+ alc883_medion_md2_automute(codec); >+} >+ >+/* toggle speaker-output according to the hp-jack state */ >+static void alc883_tagra_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ unsigned char bits; >+ >+ present = snd_hda_codec_read(codec, 0x14, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ bits = present ? HDA_AMP_MUTE : 0; >+ snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, bits); >+ snd_hda_codec_write_cache(codec, 1, 0, AC_VERB_SET_GPIO_DATA, >+ present ? 1 : 3); >+} >+ >+static void alc883_tagra_unsol_event(struct hda_codec *codec, unsigned int res) >+{ >+ if ((res >> 26) == ALC880_HP_EVENT) >+ alc883_tagra_automute(codec); >+} >+ >+/* toggle speaker-output according to the hp-jack state */ >+static void alc883_clevo_m720_hp_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ unsigned char bits; >+ >+ present = snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_PIN_SENSE, 0) >+ & AC_PINSENSE_PRESENCE; >+ bits = present ? HDA_AMP_MUTE : 0; >+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, bits); >+} >+ >+static void alc883_clevo_m720_mic_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ >+ present = snd_hda_codec_read(codec, 0x18, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, >+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); >+} >+ >+static void alc883_clevo_m720_automute(struct hda_codec *codec) >+{ >+ alc883_clevo_m720_hp_automute(codec); >+ alc883_clevo_m720_mic_automute(codec); >+} >+ >+static void alc883_clevo_m720_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ switch (res >> 26) { >+ case ALC880_HP_EVENT: >+ alc883_clevo_m720_hp_automute(codec); >+ break; >+ case ALC880_MIC_EVENT: >+ alc883_clevo_m720_mic_automute(codec); >+ break; >+ } >+} >+ >+/* toggle speaker-output according to the hp-jack state */ >+static void alc883_2ch_fujitsu_pi2515_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ unsigned char bits; >+ >+ present = snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0) >+ & AC_PINSENSE_PRESENCE; >+ bits = present ? HDA_AMP_MUTE : 0; >+ snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, bits); >+} >+ >+static void alc883_2ch_fujitsu_pi2515_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ if ((res >> 26) == ALC880_HP_EVENT) >+ alc883_2ch_fujitsu_pi2515_automute(codec); >+} >+ >+static void alc883_haier_w66_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ unsigned char bits; >+ >+ present = snd_hda_codec_read(codec, 0x1b, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ bits = present ? 0x80 : 0; >+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, >+ 0x80, bits); >+} >+ >+static void alc883_haier_w66_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ if ((res >> 26) == ALC880_HP_EVENT) >+ alc883_haier_w66_automute(codec); >+} >+ >+static void alc883_lenovo_101e_ispeaker_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ unsigned char bits; >+ >+ present = snd_hda_codec_read(codec, 0x14, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ bits = present ? HDA_AMP_MUTE : 0; >+ snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, bits); >+} >+ >+static void alc883_lenovo_101e_all_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ unsigned char bits; >+ >+ present = snd_hda_codec_read(codec, 0x1b, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ bits = present ? HDA_AMP_MUTE : 0; >+ snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, bits); >+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, bits); >+} >+ >+static void alc883_lenovo_101e_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ if ((res >> 26) == ALC880_HP_EVENT) >+ alc883_lenovo_101e_all_automute(codec); >+ if ((res >> 26) == ALC880_FRONT_EVENT) >+ alc883_lenovo_101e_ispeaker_automute(codec); >+} >+ >+/* toggle speaker-output according to the hp-jack state */ >+static void alc883_acer_aspire_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ >+ present = snd_hda_codec_read(codec, 0x14, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); >+ snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); >+} >+ >+static void alc883_acer_aspire_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ if ((res >> 26) == ALC880_HP_EVENT) >+ alc883_acer_aspire_automute(codec); >+} >+ >+static struct hda_verb alc883_acer_eapd_verbs[] = { >+ /* HP Pin: output 0 (0x0c) */ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ /* Front Pin: output 0 (0x0c) */ >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ /* eanable EAPD on medion laptop */ >+ {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, >+ {0x20, AC_VERB_SET_PROC_COEF, 0x3050}, >+ /* enable unsolicited event */ >+ {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, >+ { } >+}; >+ >+static void alc888_6st_dell_front_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ >+ present = snd_hda_codec_read(codec, 0x1b, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); >+ snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); >+ snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); >+ snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); >+} >+ >+static void alc888_6st_dell_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ switch (res >> 26) { >+ case ALC880_HP_EVENT: >+ printk("hp_event\n"); >+ alc888_6st_dell_front_automute(codec); >+ break; >+ } >+} >+ >+/* >+ * generic initialization of ADC, input mixers and output mixers >+ */ >+static struct hda_verb alc883_auto_init_verbs[] = { >+ /* >+ * Unmute ADC0-2 and set the default input to mic-in >+ */ >+ {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ >+ /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback >+ * mixer widget >+ * Note: PASD motherboards uses the Line In 2 as the input for >+ * front panel mic (mic 2) >+ */ >+ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, >+ >+ /* >+ * Set up output mixers (0x0c - 0x0f) >+ */ >+ /* set vol=0 to output mixers */ >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ /* set up input amps for analog loopback */ >+ /* Amp Indices: DAC = 0, mixer = 1 */ >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ >+ /* FIXME: use matrix-type input source selection */ >+ /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ >+ /* Input mixer1 */ >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, >+ /* {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, */ >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, >+ /* Input mixer2 */ >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, >+ /* {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, */ >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, >+ >+ { } >+}; >+ >+/* capture mixer elements */ >+static struct snd_kcontrol_new alc883_capture_mixer[] = { >+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ /* The multiple "Capture Source" controls confuse alsamixer >+ * So call somewhat different.. >+ */ >+ /* .name = "Capture Source", */ >+ .name = "Input Source", >+ .count = 2, >+ .info = alc882_mux_enum_info, >+ .get = alc882_mux_enum_get, >+ .put = alc882_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+#define alc883_loopbacks alc880_loopbacks >+#endif >+ >+/* pcm configuration: identiacal with ALC880 */ >+#define alc883_pcm_analog_playback alc880_pcm_analog_playback >+#define alc883_pcm_analog_capture alc880_pcm_analog_capture >+#define alc883_pcm_analog_alt_capture alc880_pcm_analog_alt_capture >+#define alc883_pcm_digital_playback alc880_pcm_digital_playback >+#define alc883_pcm_digital_capture alc880_pcm_digital_capture >+ >+/* >+ * configuration and preset >+ */ >+static const char *alc883_models[ALC883_MODEL_LAST] = { >+ [ALC883_3ST_2ch_DIG] = "3stack-dig", >+ [ALC883_3ST_6ch_DIG] = "3stack-6ch-dig", >+ [ALC883_3ST_6ch] = "3stack-6ch", >+ [ALC883_6ST_DIG] = "6stack-dig", >+ [ALC883_TARGA_DIG] = "targa-dig", >+ [ALC883_TARGA_2ch_DIG] = "targa-2ch-dig", >+ [ALC883_ACER] = "acer", >+ [ALC883_ACER_ASPIRE] = "acer-aspire", >+ [ALC883_MEDION] = "medion", >+ [ALC883_MEDION_MD2] = "medion-md2", >+ [ALC883_LAPTOP_EAPD] = "laptop-eapd", >+ [ALC883_LENOVO_101E_2ch] = "lenovo-101e", >+ [ALC883_LENOVO_NB0763] = "lenovo-nb0763", >+ [ALC888_LENOVO_MS7195_DIG] = "lenovo-ms7195-dig", >+ [ALC883_HAIER_W66] = "haier-w66", >+ [ALC888_3ST_HP] = "3stack-hp", >+ [ALC888_6ST_DELL] = "6stack-dell", >+ [ALC883_MITAC] = "mitac", >+ [ALC883_CLEVO_M720] = "clevo-m720", >+ [ALC883_FUJITSU_PI2515] = "fujitsu-pi2515", >+ [ALC883_3ST_6ch_INTEL] = "3stack-6ch-intel", >+ [ALC883_AUTO] = "auto", >+}; >+ >+static struct snd_pci_quirk alc883_cfg_tbl[] = { >+ SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC883_3ST_6ch_DIG), >+ SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_ACER_ASPIRE), >+ SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_ACER_ASPIRE), >+ SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_ACER_ASPIRE), >+ SND_PCI_QUIRK(0x1025, 0, "Acer laptop", ALC883_ACER), /* default Acer */ >+ SND_PCI_QUIRK(0x1028, 0x020d, "Dell Inspiron 530", ALC888_6ST_DELL), >+ SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavillion", ALC883_6ST_DIG), >+ SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP), >+ SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP), >+ SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC883_6ST_DIG), >+ SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG), >+ SND_PCI_QUIRK(0x105b, 0x0ce8, "Foxconn P35AX-S", ALC883_6ST_DIG), >+ SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC883_6ST_DIG), >+ SND_PCI_QUIRK(0x1071, 0x8253, "Mitac 8252d", ALC883_MITAC), >+ SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD), >+ SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch), >+ SND_PCI_QUIRK(0x1458, 0xa002, "MSI", ALC883_6ST_DIG), >+ SND_PCI_QUIRK(0x1462, 0x0349, "MSI", ALC883_TARGA_2ch_DIG), >+ SND_PCI_QUIRK(0x1462, 0x040d, "MSI", ALC883_TARGA_2ch_DIG), >+ SND_PCI_QUIRK(0x1462, 0x0579, "MSI", ALC883_TARGA_2ch_DIG), >+ SND_PCI_QUIRK(0x1462, 0x2fb3, "MSI", ALC883_TARGA_2ch_DIG), >+ SND_PCI_QUIRK(0x1462, 0x3729, "MSI S420", ALC883_TARGA_DIG), >+ SND_PCI_QUIRK(0x1462, 0x3783, "NEC S970", ALC883_TARGA_DIG), >+ SND_PCI_QUIRK(0x1462, 0x3b7f, "MSI", ALC883_TARGA_2ch_DIG), >+ SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG), >+ SND_PCI_QUIRK(0x1462, 0x3fc1, "MSI", ALC883_TARGA_DIG), >+ SND_PCI_QUIRK(0x1462, 0x3fc3, "MSI", ALC883_TARGA_DIG), >+ SND_PCI_QUIRK(0x1462, 0x3fcc, "MSI", ALC883_TARGA_DIG), >+ SND_PCI_QUIRK(0x1462, 0x3fdf, "MSI", ALC883_TARGA_DIG), >+ SND_PCI_QUIRK(0x1462, 0x4314, "MSI", ALC883_TARGA_DIG), >+ SND_PCI_QUIRK(0x1462, 0x4319, "MSI", ALC883_TARGA_DIG), >+ SND_PCI_QUIRK(0x1462, 0x4324, "MSI", ALC883_TARGA_DIG), >+ SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG), >+ SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG), >+ SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG), >+ SND_PCI_QUIRK(0x1462, 0x7267, "MSI", ALC883_3ST_6ch_DIG), >+ SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG), >+ SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG), >+ SND_PCI_QUIRK(0x1462, 0xa422, "MSI", ALC883_TARGA_2ch_DIG), >+ SND_PCI_QUIRK(0x147b, 0x1083, "Abit IP35-PRO", ALC883_6ST_DIG), >+ SND_PCI_QUIRK(0x1558, 0x0721, "Clevo laptop M720R", ALC883_CLEVO_M720), >+ SND_PCI_QUIRK(0x1558, 0x0722, "Clevo laptop M720SR", ALC883_CLEVO_M720), >+ SND_PCI_QUIRK(0x1558, 0, "Clevo laptop", ALC883_LAPTOP_EAPD), >+ SND_PCI_QUIRK(0x15d9, 0x8780, "Supermicro PDSBA", ALC883_3ST_6ch), >+ SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION), >+ SND_PCI_QUIRK(0x1734, 0x1108, "Fujitsu AMILO Pi2515", ALC883_FUJITSU_PI2515), >+ SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo 101e", ALC883_LENOVO_101E_2ch), >+ SND_PCI_QUIRK(0x17aa, 0x2085, "Lenovo NB0763", ALC883_LENOVO_NB0763), >+ SND_PCI_QUIRK(0x17aa, 0x3bfc, "Lenovo NB0763", ALC883_LENOVO_NB0763), >+ SND_PCI_QUIRK(0x17aa, 0x3bfd, "Lenovo NB0763", ALC883_LENOVO_NB0763), >+ SND_PCI_QUIRK(0x17c0, 0x4071, "MEDION MD2", ALC883_MEDION_MD2), >+ SND_PCI_QUIRK(0x17f2, 0x5000, "Albatron KI690-AM2", ALC883_6ST_DIG), >+ SND_PCI_QUIRK(0x1991, 0x5625, "Haier W66", ALC883_HAIER_W66), >+ SND_PCI_QUIRK(0x8086, 0x0001, "DG33BUC", ALC883_3ST_6ch_INTEL), >+ SND_PCI_QUIRK(0x8086, 0x0002, "DG33FBC", ALC883_3ST_6ch_INTEL), >+ SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC883_3ST_6ch), >+ {} >+}; >+ >+static struct alc_config_preset alc883_presets[] = { >+ [ALC883_3ST_2ch_DIG] = { >+ .mixers = { alc883_3ST_2ch_mixer }, >+ .init_verbs = { alc883_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc883_dac_nids), >+ .dac_nids = alc883_dac_nids, >+ .dig_out_nid = ALC883_DIGOUT_NID, >+ .dig_in_nid = ALC883_DIGIN_NID, >+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), >+ .channel_mode = alc883_3ST_2ch_modes, >+ .input_mux = &alc883_capture_source, >+ }, >+ [ALC883_3ST_6ch_DIG] = { >+ .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, >+ .init_verbs = { alc883_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc883_dac_nids), >+ .dac_nids = alc883_dac_nids, >+ .dig_out_nid = ALC883_DIGOUT_NID, >+ .dig_in_nid = ALC883_DIGIN_NID, >+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), >+ .channel_mode = alc883_3ST_6ch_modes, >+ .need_dac_fix = 1, >+ .input_mux = &alc883_capture_source, >+ }, >+ [ALC883_3ST_6ch] = { >+ .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, >+ .init_verbs = { alc883_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc883_dac_nids), >+ .dac_nids = alc883_dac_nids, >+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), >+ .channel_mode = alc883_3ST_6ch_modes, >+ .need_dac_fix = 1, >+ .input_mux = &alc883_capture_source, >+ }, >+ [ALC883_3ST_6ch_INTEL] = { >+ .mixers = { alc883_3ST_6ch_intel_mixer, alc883_chmode_mixer }, >+ .init_verbs = { alc883_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc883_dac_nids), >+ .dac_nids = alc883_dac_nids, >+ .dig_out_nid = ALC883_DIGOUT_NID, >+ .dig_in_nid = ALC883_DIGIN_NID, >+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_intel_modes), >+ .channel_mode = alc883_3ST_6ch_intel_modes, >+ .need_dac_fix = 1, >+ .input_mux = &alc883_3stack_6ch_intel, >+ }, >+ [ALC883_6ST_DIG] = { >+ .mixers = { alc883_base_mixer, alc883_chmode_mixer }, >+ .init_verbs = { alc883_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc883_dac_nids), >+ .dac_nids = alc883_dac_nids, >+ .dig_out_nid = ALC883_DIGOUT_NID, >+ .dig_in_nid = ALC883_DIGIN_NID, >+ .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), >+ .channel_mode = alc883_sixstack_modes, >+ .input_mux = &alc883_capture_source, >+ }, >+ [ALC883_TARGA_DIG] = { >+ .mixers = { alc883_tagra_mixer, alc883_chmode_mixer }, >+ .init_verbs = { alc883_init_verbs, alc883_tagra_verbs}, >+ .num_dacs = ARRAY_SIZE(alc883_dac_nids), >+ .dac_nids = alc883_dac_nids, >+ .dig_out_nid = ALC883_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), >+ .channel_mode = alc883_3ST_6ch_modes, >+ .need_dac_fix = 1, >+ .input_mux = &alc883_capture_source, >+ .unsol_event = alc883_tagra_unsol_event, >+ .init_hook = alc883_tagra_automute, >+ }, >+ [ALC883_TARGA_2ch_DIG] = { >+ .mixers = { alc883_tagra_2ch_mixer}, >+ .init_verbs = { alc883_init_verbs, alc883_tagra_verbs}, >+ .num_dacs = ARRAY_SIZE(alc883_dac_nids), >+ .dac_nids = alc883_dac_nids, >+ .dig_out_nid = ALC883_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), >+ .channel_mode = alc883_3ST_2ch_modes, >+ .input_mux = &alc883_capture_source, >+ .unsol_event = alc883_tagra_unsol_event, >+ .init_hook = alc883_tagra_automute, >+ }, >+ [ALC883_ACER] = { >+ .mixers = { alc883_base_mixer }, >+ /* On TravelMate laptops, GPIO 0 enables the internal speaker >+ * and the headphone jack. Turn this on and rely on the >+ * standard mute methods whenever the user wants to turn >+ * these outputs off. >+ */ >+ .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc883_dac_nids), >+ .dac_nids = alc883_dac_nids, >+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), >+ .channel_mode = alc883_3ST_2ch_modes, >+ .input_mux = &alc883_capture_source, >+ }, >+ [ALC883_ACER_ASPIRE] = { >+ .mixers = { alc883_acer_aspire_mixer }, >+ .init_verbs = { alc883_init_verbs, alc883_acer_eapd_verbs }, >+ .num_dacs = ARRAY_SIZE(alc883_dac_nids), >+ .dac_nids = alc883_dac_nids, >+ .dig_out_nid = ALC883_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), >+ .channel_mode = alc883_3ST_2ch_modes, >+ .input_mux = &alc883_capture_source, >+ .unsol_event = alc883_acer_aspire_unsol_event, >+ .init_hook = alc883_acer_aspire_automute, >+ }, >+ [ALC883_MEDION] = { >+ .mixers = { alc883_fivestack_mixer, >+ alc883_chmode_mixer }, >+ .init_verbs = { alc883_init_verbs, >+ alc883_medion_eapd_verbs }, >+ .num_dacs = ARRAY_SIZE(alc883_dac_nids), >+ .dac_nids = alc883_dac_nids, >+ .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), >+ .channel_mode = alc883_sixstack_modes, >+ .input_mux = &alc883_capture_source, >+ }, >+ [ALC883_MEDION_MD2] = { >+ .mixers = { alc883_medion_md2_mixer}, >+ .init_verbs = { alc883_init_verbs, alc883_medion_md2_verbs}, >+ .num_dacs = ARRAY_SIZE(alc883_dac_nids), >+ .dac_nids = alc883_dac_nids, >+ .dig_out_nid = ALC883_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), >+ .channel_mode = alc883_3ST_2ch_modes, >+ .input_mux = &alc883_capture_source, >+ .unsol_event = alc883_medion_md2_unsol_event, >+ .init_hook = alc883_medion_md2_automute, >+ }, >+ [ALC883_LAPTOP_EAPD] = { >+ .mixers = { alc883_base_mixer }, >+ .init_verbs = { alc883_init_verbs, alc882_eapd_verbs }, >+ .num_dacs = ARRAY_SIZE(alc883_dac_nids), >+ .dac_nids = alc883_dac_nids, >+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), >+ .channel_mode = alc883_3ST_2ch_modes, >+ .input_mux = &alc883_capture_source, >+ }, >+ [ALC883_CLEVO_M720] = { >+ .mixers = { alc883_clevo_m720_mixer }, >+ .init_verbs = { alc883_init_verbs, alc883_clevo_m720_verbs }, >+ .num_dacs = ARRAY_SIZE(alc883_dac_nids), >+ .dac_nids = alc883_dac_nids, >+ .dig_out_nid = ALC883_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), >+ .channel_mode = alc883_3ST_2ch_modes, >+ .input_mux = &alc883_capture_source, >+ .unsol_event = alc883_clevo_m720_unsol_event, >+ .init_hook = alc883_clevo_m720_automute, >+ }, >+ [ALC883_LENOVO_101E_2ch] = { >+ .mixers = { alc883_lenovo_101e_2ch_mixer}, >+ .init_verbs = { alc883_init_verbs, alc883_lenovo_101e_verbs}, >+ .num_dacs = ARRAY_SIZE(alc883_dac_nids), >+ .dac_nids = alc883_dac_nids, >+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), >+ .channel_mode = alc883_3ST_2ch_modes, >+ .input_mux = &alc883_lenovo_101e_capture_source, >+ .unsol_event = alc883_lenovo_101e_unsol_event, >+ .init_hook = alc883_lenovo_101e_all_automute, >+ }, >+ [ALC883_LENOVO_NB0763] = { >+ .mixers = { alc883_lenovo_nb0763_mixer }, >+ .init_verbs = { alc883_init_verbs, alc883_lenovo_nb0763_verbs}, >+ .num_dacs = ARRAY_SIZE(alc883_dac_nids), >+ .dac_nids = alc883_dac_nids, >+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), >+ .channel_mode = alc883_3ST_2ch_modes, >+ .need_dac_fix = 1, >+ .input_mux = &alc883_lenovo_nb0763_capture_source, >+ .unsol_event = alc883_medion_md2_unsol_event, >+ .init_hook = alc883_medion_md2_automute, >+ }, >+ [ALC888_LENOVO_MS7195_DIG] = { >+ .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, >+ .init_verbs = { alc883_init_verbs, alc888_lenovo_ms7195_verbs}, >+ .num_dacs = ARRAY_SIZE(alc883_dac_nids), >+ .dac_nids = alc883_dac_nids, >+ .dig_out_nid = ALC883_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), >+ .channel_mode = alc883_3ST_6ch_modes, >+ .need_dac_fix = 1, >+ .input_mux = &alc883_capture_source, >+ .unsol_event = alc883_lenovo_ms7195_unsol_event, >+ .init_hook = alc888_lenovo_ms7195_front_automute, >+ }, >+ [ALC883_HAIER_W66] = { >+ .mixers = { alc883_tagra_2ch_mixer}, >+ .init_verbs = { alc883_init_verbs, alc883_haier_w66_verbs}, >+ .num_dacs = ARRAY_SIZE(alc883_dac_nids), >+ .dac_nids = alc883_dac_nids, >+ .dig_out_nid = ALC883_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), >+ .channel_mode = alc883_3ST_2ch_modes, >+ .input_mux = &alc883_capture_source, >+ .unsol_event = alc883_haier_w66_unsol_event, >+ .init_hook = alc883_haier_w66_automute, >+ }, >+ [ALC888_3ST_HP] = { >+ .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, >+ .init_verbs = { alc883_init_verbs, alc888_3st_hp_verbs }, >+ .num_dacs = ARRAY_SIZE(alc883_dac_nids), >+ .dac_nids = alc883_dac_nids, >+ .num_channel_mode = ARRAY_SIZE(alc888_3st_hp_modes), >+ .channel_mode = alc888_3st_hp_modes, >+ .need_dac_fix = 1, >+ .input_mux = &alc883_capture_source, >+ }, >+ [ALC888_6ST_DELL] = { >+ .mixers = { alc883_base_mixer, alc883_chmode_mixer }, >+ .init_verbs = { alc883_init_verbs, alc888_6st_dell_verbs }, >+ .num_dacs = ARRAY_SIZE(alc883_dac_nids), >+ .dac_nids = alc883_dac_nids, >+ .dig_out_nid = ALC883_DIGOUT_NID, >+ .dig_in_nid = ALC883_DIGIN_NID, >+ .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), >+ .channel_mode = alc883_sixstack_modes, >+ .input_mux = &alc883_capture_source, >+ .unsol_event = alc888_6st_dell_unsol_event, >+ .init_hook = alc888_6st_dell_front_automute, >+ }, >+ [ALC883_MITAC] = { >+ .mixers = { alc883_mitac_mixer }, >+ .init_verbs = { alc883_init_verbs, alc883_mitac_verbs }, >+ .num_dacs = ARRAY_SIZE(alc883_dac_nids), >+ .dac_nids = alc883_dac_nids, >+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), >+ .channel_mode = alc883_3ST_2ch_modes, >+ .input_mux = &alc883_capture_source, >+ .unsol_event = alc883_mitac_unsol_event, >+ .init_hook = alc883_mitac_automute, >+ }, >+ [ALC883_FUJITSU_PI2515] = { >+ .mixers = { alc883_2ch_fujitsu_pi2515_mixer }, >+ .init_verbs = { alc883_init_verbs, >+ alc883_2ch_fujitsu_pi2515_verbs}, >+ .num_dacs = ARRAY_SIZE(alc883_dac_nids), >+ .dac_nids = alc883_dac_nids, >+ .dig_out_nid = ALC883_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), >+ .channel_mode = alc883_3ST_2ch_modes, >+ .input_mux = &alc883_fujitsu_pi2515_capture_source, >+ .unsol_event = alc883_2ch_fujitsu_pi2515_unsol_event, >+ .init_hook = alc883_2ch_fujitsu_pi2515_automute, >+ }, >+}; >+ >+ >+/* >+ * BIOS auto configuration >+ */ >+static void alc883_auto_set_output_and_unmute(struct hda_codec *codec, >+ hda_nid_t nid, int pin_type, >+ int dac_idx) >+{ >+ /* set as output */ >+ struct alc_spec *spec = codec->spec; >+ int idx; >+ >+ alc_set_pin_output(codec, nid, pin_type); >+ if (spec->multiout.dac_nids[dac_idx] == 0x25) >+ idx = 4; >+ else >+ idx = spec->multiout.dac_nids[dac_idx] - 2; >+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx); >+ >+} >+ >+static void alc883_auto_init_multi_out(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ int i; >+ >+ alc_subsystem_id(codec, 0x15, 0x1b, 0x14); >+ for (i = 0; i <= HDA_SIDE; i++) { >+ hda_nid_t nid = spec->autocfg.line_out_pins[i]; >+ int pin_type = get_pin_type(spec->autocfg.line_out_type); >+ if (nid) >+ alc883_auto_set_output_and_unmute(codec, nid, pin_type, >+ i); >+ } >+} >+ >+static void alc883_auto_init_hp_out(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ hda_nid_t pin; >+ >+ pin = spec->autocfg.hp_pins[0]; >+ if (pin) /* connect to front */ >+ /* use dac 0 */ >+ alc883_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); >+ pin = spec->autocfg.speaker_pins[0]; >+ if (pin) >+ alc883_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); >+} >+ >+#define alc883_is_input_pin(nid) alc880_is_input_pin(nid) >+#define ALC883_PIN_CD_NID ALC880_PIN_CD_NID >+ >+static void alc883_auto_init_analog_input(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ int i; >+ >+ for (i = 0; i < AUTO_PIN_LAST; i++) { >+ hda_nid_t nid = spec->autocfg.input_pins[i]; >+ if (alc883_is_input_pin(nid)) { >+ snd_hda_codec_write(codec, nid, 0, >+ AC_VERB_SET_PIN_WIDGET_CONTROL, >+ (i <= AUTO_PIN_FRONT_MIC ? >+ PIN_VREF80 : PIN_IN)); >+ if (nid != ALC883_PIN_CD_NID) >+ snd_hda_codec_write(codec, nid, 0, >+ AC_VERB_SET_AMP_GAIN_MUTE, >+ AMP_OUT_MUTE); >+ } >+ } >+} >+ >+/* almost identical with ALC880 parser... */ >+static int alc883_parse_auto_config(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ int err = alc880_parse_auto_config(codec); >+ >+ if (err < 0) >+ return err; >+ else if (!err) >+ return 0; /* no config found */ >+ >+ err = alc_auto_add_mic_boost(codec); >+ if (err < 0) >+ return err; >+ >+ /* hack - override the init verbs */ >+ spec->init_verbs[0] = alc883_auto_init_verbs; >+ spec->mixers[spec->num_mixers] = alc883_capture_mixer; >+ spec->num_mixers++; >+ >+ return 1; /* config found */ >+} >+ >+/* additional initialization for auto-configuration model */ >+static void alc883_auto_init(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ alc883_auto_init_multi_out(codec); >+ alc883_auto_init_hp_out(codec); >+ alc883_auto_init_analog_input(codec); >+ if (spec->unsol_event) >+ alc_sku_automute(codec); >+} >+ >+static int patch_alc883(struct hda_codec *codec) >+{ >+ struct alc_spec *spec; >+ int err, board_config; >+ >+ spec = kzalloc(sizeof(*spec), GFP_KERNEL); >+ if (spec == NULL) >+ return -ENOMEM; >+ >+ codec->spec = spec; >+ >+ alc_fix_pll_init(codec, 0x20, 0x0a, 10); >+ >+ board_config = snd_hda_check_board_config(codec, ALC883_MODEL_LAST, >+ alc883_models, >+ alc883_cfg_tbl); >+ if (board_config < 0) { >+ printk(KERN_INFO "hda_codec: Unknown model for ALC883, " >+ "trying auto-probe from BIOS...\n"); >+ board_config = ALC883_AUTO; >+ } >+ >+ if (board_config == ALC883_AUTO) { >+ /* automatic parse from the BIOS config */ >+ err = alc883_parse_auto_config(codec); >+ if (err < 0) { >+ alc_free(codec); >+ return err; >+ } else if (!err) { >+ printk(KERN_INFO >+ "hda_codec: Cannot set up configuration " >+ "from BIOS. Using base mode...\n"); >+ board_config = ALC883_3ST_2ch_DIG; >+ } >+ } >+ >+ if (board_config != ALC883_AUTO) >+ setup_preset(spec, &alc883_presets[board_config]); >+ >+ switch (codec->vendor_id) { >+ case 0x10ec0888: >+ spec->stream_name_analog = "ALC888 Analog"; >+ spec->stream_name_digital = "ALC888 Digital"; >+ break; >+ case 0x10ec0889: >+ spec->stream_name_analog = "ALC889 Analog"; >+ spec->stream_name_digital = "ALC889 Digital"; >+ break; >+ default: >+ spec->stream_name_analog = "ALC883 Analog"; >+ spec->stream_name_digital = "ALC883 Digital"; >+ break; >+ } >+ >+ spec->stream_analog_playback = &alc883_pcm_analog_playback; >+ spec->stream_analog_capture = &alc883_pcm_analog_capture; >+ spec->stream_analog_alt_capture = &alc883_pcm_analog_alt_capture; >+ >+ spec->stream_digital_playback = &alc883_pcm_digital_playback; >+ spec->stream_digital_capture = &alc883_pcm_digital_capture; >+ >+ spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids); >+ spec->adc_nids = alc883_adc_nids; >+ spec->capsrc_nids = alc883_capsrc_nids; >+ >+ spec->vmaster_nid = 0x0c; >+ >+ codec->patch_ops = alc_patch_ops; >+ if (board_config == ALC883_AUTO) >+ spec->init_hook = alc883_auto_init; >+ else if (codec->vendor_id == 0x10ec0888) >+ spec->init_hook = alc888_coef_init; >+ >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+ if (!spec->loopback.amplist) >+ spec->loopback.amplist = alc883_loopbacks; >+#endif >+ >+ return 0; >+} >+ >+/* >+ * ALC262 support >+ */ >+ >+#define ALC262_DIGOUT_NID ALC880_DIGOUT_NID >+#define ALC262_DIGIN_NID ALC880_DIGIN_NID >+ >+#define alc262_dac_nids alc260_dac_nids >+#define alc262_adc_nids alc882_adc_nids >+#define alc262_adc_nids_alt alc882_adc_nids_alt >+#define alc262_capsrc_nids alc882_capsrc_nids >+#define alc262_capsrc_nids_alt alc882_capsrc_nids_alt >+ >+#define alc262_modes alc260_modes >+#define alc262_capture_source alc882_capture_source >+ >+static struct snd_kcontrol_new alc262_base_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), >+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), >+ /* HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x0b, 0x05, HDA_INPUT), >+ HDA_CODEC_MUTE("PC Beep Playback Switch", 0x0b, 0x05, HDA_INPUT), */ >+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0D, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT), >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc262_hippo1_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), >+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), >+ /* HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x0b, 0x05, HDA_INPUT), >+ HDA_CODEC_MUTE("PC Beep Playback Switch", 0x0b, 0x05, HDA_INPUT), */ >+ /*HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0D, 0x0, HDA_OUTPUT),*/ >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), >+ { } /* end */ >+}; >+ >+/* update HP, line and mono-out pins according to the master switch */ >+static void alc262_hp_master_update(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ int val = spec->master_sw; >+ >+ /* HP & line-out */ >+ snd_hda_codec_write_cache(codec, 0x1b, 0, >+ AC_VERB_SET_PIN_WIDGET_CONTROL, >+ val ? PIN_HP : 0); >+ snd_hda_codec_write_cache(codec, 0x15, 0, >+ AC_VERB_SET_PIN_WIDGET_CONTROL, >+ val ? PIN_HP : 0); >+ /* mono (speaker) depending on the HP jack sense */ >+ val = val && !spec->jack_present; >+ snd_hda_codec_write_cache(codec, 0x16, 0, >+ AC_VERB_SET_PIN_WIDGET_CONTROL, >+ val ? PIN_OUT : 0); >+} >+ >+static void alc262_hp_bpc_automute(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ unsigned int presence; >+ presence = snd_hda_codec_read(codec, 0x1b, 0, >+ AC_VERB_GET_PIN_SENSE, 0); >+ spec->jack_present = !!(presence & AC_PINSENSE_PRESENCE); >+ alc262_hp_master_update(codec); >+} >+ >+static void alc262_hp_bpc_unsol_event(struct hda_codec *codec, unsigned int res) >+{ >+ if ((res >> 26) != ALC880_HP_EVENT) >+ return; >+ alc262_hp_bpc_automute(codec); >+} >+ >+static void alc262_hp_wildwest_automute(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ unsigned int presence; >+ presence = snd_hda_codec_read(codec, 0x15, 0, >+ AC_VERB_GET_PIN_SENSE, 0); >+ spec->jack_present = !!(presence & AC_PINSENSE_PRESENCE); >+ alc262_hp_master_update(codec); >+} >+ >+static void alc262_hp_wildwest_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ if ((res >> 26) != ALC880_HP_EVENT) >+ return; >+ alc262_hp_wildwest_automute(codec); >+} >+ >+static int alc262_hp_master_sw_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.integer.value = spec->master_sw; >+ return 0; >+} >+ >+static int alc262_hp_master_sw_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 val = !!*ucontrol->value.integer.value; >+ >+ if (val == spec->master_sw) >+ return 0; >+ spec->master_sw = val; >+ alc262_hp_master_update(codec); >+ return 1; >+} >+ >+static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = { >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Master Playback Switch", >+ .info = snd_ctl_boolean_mono_info, >+ .get = alc262_hp_master_sw_get, >+ .put = alc262_hp_master_sw_put, >+ }, >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 2, 0x0, >+ HDA_OUTPUT), >+ HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 2, 0x0, >+ HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), >+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x0b, 0x05, HDA_INPUT), >+ HDA_CODEC_MUTE("PC Beep Playback Switch", 0x0b, 0x05, HDA_INPUT), >+ HDA_CODEC_VOLUME("AUX IN Playback Volume", 0x0b, 0x06, HDA_INPUT), >+ HDA_CODEC_MUTE("AUX IN Playback Switch", 0x0b, 0x06, HDA_INPUT), >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc262_HP_BPC_WildWest_mixer[] = { >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Master Playback Switch", >+ .info = snd_ctl_boolean_mono_info, >+ .get = alc262_hp_master_sw_get, >+ .put = alc262_hp_master_sw_put, >+ }, >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 2, 0x0, >+ HDA_OUTPUT), >+ HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 2, 0x0, >+ HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Boost", 0x1a, 0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x0b, 0x05, HDA_INPUT), >+ HDA_CODEC_MUTE("PC Beep Playback Switch", 0x0b, 0x05, HDA_INPUT), >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc262_HP_BPC_WildWest_option_mixer[] = { >+ HDA_CODEC_VOLUME("Rear Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Rear Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Rear Mic Boost", 0x18, 0, HDA_INPUT), >+ { } /* end */ >+}; >+ >+/* mute/unmute internal speaker according to the hp jack and mute state */ >+static void alc262_hp_t5735_automute(struct hda_codec *codec, int force) >+{ >+ struct alc_spec *spec = codec->spec; >+ >+ if (force || !spec->sense_updated) { >+ unsigned int present; >+ present = snd_hda_codec_read(codec, 0x15, 0, >+ AC_VERB_GET_PIN_SENSE, 0); >+ spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0; >+ spec->sense_updated = 1; >+ } >+ snd_hda_codec_amp_stereo(codec, 0x0c, HDA_OUTPUT, 0, HDA_AMP_MUTE, >+ spec->jack_present ? HDA_AMP_MUTE : 0); >+} >+ >+static void alc262_hp_t5735_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ if ((res >> 26) != ALC880_HP_EVENT) >+ return; >+ alc262_hp_t5735_automute(codec, 1); >+} >+ >+static void alc262_hp_t5735_init_hook(struct hda_codec *codec) >+{ >+ alc262_hp_t5735_automute(codec, 1); >+} >+ >+static struct snd_kcontrol_new alc262_hp_t5735_mixer[] = { >+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), >+ { } /* end */ >+}; >+ >+static struct hda_verb alc262_hp_t5735_verbs[] = { >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ >+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, >+ { } >+}; >+ >+static struct snd_kcontrol_new alc262_hp_rp5700_mixer[] = { >+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0e, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x16, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT), >+ { } /* end */ >+}; >+ >+static struct hda_verb alc262_hp_rp5700_verbs[] = { >+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x00 << 8))}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x00 << 8))}, >+ {} >+}; >+ >+static struct hda_input_mux alc262_hp_rp5700_capture_source = { >+ .num_items = 1, >+ .items = { >+ { "Line", 0x1 }, >+ }, >+}; >+ >+/* bind hp and internal speaker mute (with plug check) */ >+static int alc262_sony_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 hp mute */ >+ change = snd_hda_codec_amp_update(codec, 0x15, 0, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, >+ valp[0] ? 0 : HDA_AMP_MUTE); >+ change |= snd_hda_codec_amp_update(codec, 0x15, 1, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, >+ valp[1] ? 0 : HDA_AMP_MUTE); >+ if (change) { >+ /* change speaker according to HP jack state */ >+ struct alc_spec *spec = codec->spec; >+ unsigned int mute; >+ if (spec->jack_present) >+ mute = HDA_AMP_MUTE; >+ else >+ mute = snd_hda_codec_amp_read(codec, 0x15, 0, >+ HDA_OUTPUT, 0); >+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, mute); >+ } >+ return change; >+} >+ >+static struct snd_kcontrol_new alc262_sony_mixer[] = { >+ HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Master Playback Switch", >+ .info = snd_hda_mixer_amp_switch_info, >+ .get = snd_hda_mixer_amp_switch_get, >+ .put = alc262_sony_master_sw_put, >+ .private_value = HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT), >+ }, >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), >+ HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc262_benq_t31_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), >+ HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), >+ { } /* end */ >+}; >+ >+#define alc262_capture_mixer alc882_capture_mixer >+#define alc262_capture_alt_mixer alc882_capture_alt_mixer >+ >+/* >+ * generic initialization of ADC, input mixers and output mixers >+ */ >+static struct hda_verb alc262_init_verbs[] = { >+ /* >+ * Unmute ADC0-2 and set the default input to mic-in >+ */ >+ {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ >+ /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback >+ * mixer widget >+ * Note: PASD motherboards uses the Line In 2 as the input for >+ * front panel mic (mic 2) >+ */ >+ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, >+ >+ /* >+ * Set up output mixers (0x0c - 0x0e) >+ */ >+ /* set vol=0 to output mixers */ >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ /* set up input amps for analog loopback */ >+ /* Amp Indices: DAC = 0, mixer = 1 */ >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, >+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, >+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, >+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, >+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, >+ >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, >+ >+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ >+ /* FIXME: use matrix-type input source selection */ >+ /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ >+ /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, >+ /* Input mixer2 */ >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, >+ /* Input mixer3 */ >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, >+ >+ { } >+}; >+ >+static struct hda_verb alc262_hippo_unsol_verbs[] = { >+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {} >+}; >+ >+static struct hda_verb alc262_hippo1_unsol_verbs[] = { >+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, >+ {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, >+ >+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, >+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {} >+}; >+ >+static struct hda_verb alc262_sony_unsol_verbs[] = { >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, >+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, // Front Mic >+ >+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+}; >+ >+/* mute/unmute internal speaker according to the hp jack and mute state */ >+static void alc262_hippo_automute(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ unsigned int mute; >+ unsigned int present; >+ >+ /* need to execute and sync at first */ >+ snd_hda_codec_read(codec, 0x15, 0, AC_VERB_SET_PIN_SENSE, 0); >+ present = snd_hda_codec_read(codec, 0x15, 0, >+ AC_VERB_GET_PIN_SENSE, 0); >+ spec->jack_present = (present & 0x80000000) != 0; >+ if (spec->jack_present) { >+ /* mute internal speaker */ >+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, HDA_AMP_MUTE); >+ } else { >+ /* unmute internal speaker if necessary */ >+ mute = snd_hda_codec_amp_read(codec, 0x15, 0, HDA_OUTPUT, 0); >+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, mute); >+ } >+} >+ >+/* unsolicited event for HP jack sensing */ >+static void alc262_hippo_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ if ((res >> 26) != ALC880_HP_EVENT) >+ return; >+ alc262_hippo_automute(codec); >+} >+ >+static void alc262_hippo1_automute(struct hda_codec *codec) >+{ >+ unsigned int mute; >+ unsigned int present; >+ >+ snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0); >+ present = snd_hda_codec_read(codec, 0x1b, 0, >+ AC_VERB_GET_PIN_SENSE, 0); >+ present = (present & 0x80000000) != 0; >+ if (present) { >+ /* mute internal speaker */ >+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, HDA_AMP_MUTE); >+ } else { >+ /* unmute internal speaker if necessary */ >+ mute = snd_hda_codec_amp_read(codec, 0x1b, 0, HDA_OUTPUT, 0); >+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, mute); >+ } >+} >+ >+/* unsolicited event for HP jack sensing */ >+static void alc262_hippo1_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ if ((res >> 26) != ALC880_HP_EVENT) >+ return; >+ alc262_hippo1_automute(codec); >+} >+ >+/* >+ * fujitsu model >+ * 0x14 = headphone/spdif-out, 0x15 = internal speaker, >+ * 0x1b = port replicator headphone out >+ */ >+ >+#define ALC_HP_EVENT 0x37 >+ >+static struct hda_verb alc262_fujitsu_unsol_verbs[] = { >+ {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, >+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {} >+}; >+ >+static struct hda_verb alc262_lenovo_3000_unsol_verbs[] = { >+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, >+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {} >+}; >+ >+static struct hda_input_mux alc262_fujitsu_capture_source = { >+ .num_items = 3, >+ .items = { >+ { "Mic", 0x0 }, >+ { "Int Mic", 0x1 }, >+ { "CD", 0x4 }, >+ }, >+}; >+ >+static struct hda_input_mux alc262_HP_capture_source = { >+ .num_items = 5, >+ .items = { >+ { "Mic", 0x0 }, >+ { "Front Mic", 0x1 }, >+ { "Line", 0x2 }, >+ { "CD", 0x4 }, >+ { "AUX IN", 0x6 }, >+ }, >+}; >+ >+static struct hda_input_mux alc262_HP_D7000_capture_source = { >+ .num_items = 4, >+ .items = { >+ { "Mic", 0x0 }, >+ { "Front Mic", 0x2 }, >+ { "Line", 0x1 }, >+ { "CD", 0x4 }, >+ }, >+}; >+ >+/* mute/unmute internal speaker according to the hp jacks and mute state */ >+static void alc262_fujitsu_automute(struct hda_codec *codec, int force) >+{ >+ struct alc_spec *spec = codec->spec; >+ unsigned int mute; >+ >+ if (force || !spec->sense_updated) { >+ unsigned int present; >+ /* need to execute and sync at first */ >+ snd_hda_codec_read(codec, 0x14, 0, AC_VERB_SET_PIN_SENSE, 0); >+ /* check laptop HP jack */ >+ present = snd_hda_codec_read(codec, 0x14, 0, >+ AC_VERB_GET_PIN_SENSE, 0); >+ /* need to execute and sync at first */ >+ snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0); >+ /* check docking HP jack */ >+ present |= snd_hda_codec_read(codec, 0x1b, 0, >+ AC_VERB_GET_PIN_SENSE, 0); >+ if (present & AC_PINSENSE_PRESENCE) >+ spec->jack_present = 1; >+ else >+ spec->jack_present = 0; >+ spec->sense_updated = 1; >+ } >+ /* unmute internal speaker only if both HPs are unplugged and >+ * master switch is on >+ */ >+ if (spec->jack_present) >+ mute = HDA_AMP_MUTE; >+ else >+ mute = snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0); >+ snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, mute); >+} >+ >+/* unsolicited event for HP jack sensing */ >+static void alc262_fujitsu_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ if ((res >> 26) != ALC_HP_EVENT) >+ return; >+ alc262_fujitsu_automute(codec, 1); >+} >+ >+static void alc262_fujitsu_init_hook(struct hda_codec *codec) >+{ >+ alc262_fujitsu_automute(codec, 1); >+} >+ >+/* bind volumes of both NID 0x0c and 0x0d */ >+static struct hda_bind_ctls alc262_fujitsu_bind_master_vol = { >+ .ops = &snd_hda_bind_vol, >+ .values = { >+ HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT), >+ HDA_COMPOSE_AMP_VAL(0x0d, 3, 0, HDA_OUTPUT), >+ 0 >+ }, >+}; >+ >+/* mute/unmute internal speaker according to the hp jack and mute state */ >+static void alc262_lenovo_3000_automute(struct hda_codec *codec, int force) >+{ >+ struct alc_spec *spec = codec->spec; >+ unsigned int mute; >+ >+ if (force || !spec->sense_updated) { >+ unsigned int present_int_hp; >+ /* need to execute and sync at first */ >+ snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0); >+ present_int_hp = snd_hda_codec_read(codec, 0x1b, 0, >+ AC_VERB_GET_PIN_SENSE, 0); >+ spec->jack_present = (present_int_hp & 0x80000000) != 0; >+ spec->sense_updated = 1; >+ } >+ if (spec->jack_present) { >+ /* mute internal speaker */ >+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, HDA_AMP_MUTE); >+ snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, HDA_AMP_MUTE); >+ } else { >+ /* unmute internal speaker if necessary */ >+ mute = snd_hda_codec_amp_read(codec, 0x1b, 0, HDA_OUTPUT, 0); >+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, mute); >+ snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, mute); >+ } >+} >+ >+/* unsolicited event for HP jack sensing */ >+static void alc262_lenovo_3000_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ if ((res >> 26) != ALC_HP_EVENT) >+ return; >+ alc262_lenovo_3000_automute(codec, 1); >+} >+ >+/* bind hp and internal speaker mute (with plug check) */ >+static int alc262_fujitsu_master_sw_put(struct snd_kcontrol *kcontrol, >+ struct snd_ctl_elem_value *ucontrol) >+{ >+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); >+ long *valp = ucontrol->value.integer.value; >+ int change; >+ >+ change = snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, >+ valp ? 0 : HDA_AMP_MUTE); >+ change |= snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, >+ valp ? 0 : HDA_AMP_MUTE); >+ >+ if (change) >+ alc262_fujitsu_automute(codec, 0); >+ return change; >+} >+ >+static struct snd_kcontrol_new alc262_fujitsu_mixer[] = { >+ HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Master Playback Switch", >+ .info = snd_hda_mixer_amp_switch_info, >+ .get = snd_hda_mixer_amp_switch_get, >+ .put = alc262_fujitsu_master_sw_put, >+ .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), >+ }, >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("PC Speaker Volume", 0x0b, 0x05, HDA_INPUT), >+ HDA_CODEC_MUTE("PC Speaker Switch", 0x0b, 0x05, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ { } /* end */ >+}; >+ >+/* bind hp and internal speaker mute (with plug check) */ >+static int alc262_lenovo_3000_master_sw_put(struct snd_kcontrol *kcontrol, >+ struct snd_ctl_elem_value *ucontrol) >+{ >+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); >+ long *valp = ucontrol->value.integer.value; >+ int change; >+ >+ change = snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, >+ valp ? 0 : HDA_AMP_MUTE); >+ >+ if (change) >+ alc262_lenovo_3000_automute(codec, 0); >+ return change; >+} >+ >+static struct snd_kcontrol_new alc262_lenovo_3000_mixer[] = { >+ HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Master Playback Switch", >+ .info = snd_hda_mixer_amp_switch_info, >+ .get = snd_hda_mixer_amp_switch_get, >+ .put = alc262_lenovo_3000_master_sw_put, >+ .private_value = HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT), >+ }, >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ { } /* end */ >+}; >+ >+/* additional init verbs for Benq laptops */ >+static struct hda_verb alc262_EAPD_verbs[] = { >+ {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, >+ {0x20, AC_VERB_SET_PROC_COEF, 0x3070}, >+ {} >+}; >+ >+static struct hda_verb alc262_benq_t31_EAPD_verbs[] = { >+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, >+ >+ {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, >+ {0x20, AC_VERB_SET_PROC_COEF, 0x3050}, >+ {} >+}; >+ >+/* Samsung Q1 Ultra Vista model setup */ >+static struct snd_kcontrol_new alc262_ultra_mixer[] = { >+ HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x19, 0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Headphone Mic Boost", 0x15, 0, HDA_INPUT), >+ { } /* end */ >+}; >+ >+static struct hda_verb alc262_ultra_verbs[] = { >+ /* output mixer */ >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ /* speaker */ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ /* HP */ >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, >+ /* internal mic */ >+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ /* ADC, choose mic */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(8)}, >+ {} >+}; >+ >+/* mute/unmute internal speaker according to the hp jack and mute state */ >+static void alc262_ultra_automute(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ unsigned int mute; >+ >+ mute = 0; >+ /* auto-mute only when HP is used as HP */ >+ if (!spec->cur_mux[0]) { >+ unsigned int present; >+ /* need to execute and sync at first */ >+ snd_hda_codec_read(codec, 0x15, 0, AC_VERB_SET_PIN_SENSE, 0); >+ present = snd_hda_codec_read(codec, 0x15, 0, >+ AC_VERB_GET_PIN_SENSE, 0); >+ spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0; >+ if (spec->jack_present) >+ mute = HDA_AMP_MUTE; >+ } >+ /* mute/unmute internal speaker */ >+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, mute); >+ /* mute/unmute HP */ >+ snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, mute ? 0 : HDA_AMP_MUTE); >+} >+ >+/* unsolicited event for HP jack sensing */ >+static void alc262_ultra_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ if ((res >> 26) != ALC880_HP_EVENT) >+ return; >+ alc262_ultra_automute(codec); >+} >+ >+static struct hda_input_mux alc262_ultra_capture_source = { >+ .num_items = 2, >+ .items = { >+ { "Mic", 0x1 }, >+ { "Headphone", 0x7 }, >+ }, >+}; >+ >+static int alc262_ultra_mux_enum_put(struct snd_kcontrol *kcontrol, >+ struct snd_ctl_elem_value *ucontrol) >+{ >+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); >+ struct alc_spec *spec = codec->spec; >+ int ret; >+ >+ ret = alc882_mux_enum_put(kcontrol, ucontrol); >+ if (!ret) >+ return 0; >+ /* reprogram the HP pin as mic or HP according to the input source */ >+ snd_hda_codec_write_cache(codec, 0x15, 0, >+ AC_VERB_SET_PIN_WIDGET_CONTROL, >+ spec->cur_mux[0] ? PIN_VREF80 : PIN_HP); >+ alc262_ultra_automute(codec); /* mute/unmute HP */ >+ return ret; >+} >+ >+static struct snd_kcontrol_new alc262_ultra_capture_mixer[] = { >+ HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Capture Source", >+ .info = alc882_mux_enum_info, >+ .get = alc882_mux_enum_get, >+ .put = alc262_ultra_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+/* add playback controls from the parsed DAC table */ >+static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, >+ const struct auto_pin_cfg *cfg) >+{ >+ hda_nid_t nid; >+ int err; >+ >+ spec->multiout.num_dacs = 1; /* only use one dac */ >+ spec->multiout.dac_nids = spec->private_dac_nids; >+ spec->multiout.dac_nids[0] = 2; >+ >+ nid = cfg->line_out_pins[0]; >+ if (nid) { >+ err = add_control(spec, ALC_CTL_WIDGET_VOL, >+ "Front Playback Volume", >+ HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ err = add_control(spec, ALC_CTL_WIDGET_MUTE, >+ "Front Playback Switch", >+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ } >+ >+ nid = cfg->speaker_pins[0]; >+ if (nid) { >+ if (nid == 0x16) { >+ err = add_control(spec, ALC_CTL_WIDGET_VOL, >+ "Speaker Playback Volume", >+ HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ err = add_control(spec, ALC_CTL_WIDGET_MUTE, >+ "Speaker Playback Switch", >+ HDA_COMPOSE_AMP_VAL(nid, 2, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ } else { >+ err = add_control(spec, ALC_CTL_WIDGET_MUTE, >+ "Speaker Playback Switch", >+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ } >+ } >+ nid = cfg->hp_pins[0]; >+ if (nid) { >+ /* spec->multiout.hp_nid = 2; */ >+ if (nid == 0x16) { >+ err = add_control(spec, ALC_CTL_WIDGET_VOL, >+ "Headphone Playback Volume", >+ HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ err = add_control(spec, ALC_CTL_WIDGET_MUTE, >+ "Headphone Playback Switch", >+ HDA_COMPOSE_AMP_VAL(nid, 2, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ } else { >+ err = add_control(spec, ALC_CTL_WIDGET_MUTE, >+ "Headphone Playback Switch", >+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ } >+ } >+ return 0; >+} >+ >+/* identical with ALC880 */ >+#define alc262_auto_create_analog_input_ctls \ >+ alc880_auto_create_analog_input_ctls >+ >+/* >+ * generic initialization of ADC, input mixers and output mixers >+ */ >+static struct hda_verb alc262_volume_init_verbs[] = { >+ /* >+ * Unmute ADC0-2 and set the default input to mic-in >+ */ >+ {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ >+ /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback >+ * mixer widget >+ * Note: PASD motherboards uses the Line In 2 as the input for >+ * front panel mic (mic 2) >+ */ >+ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, >+ >+ /* >+ * Set up output mixers (0x0c - 0x0f) >+ */ >+ /* set vol=0 to output mixers */ >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ >+ /* set up input amps for analog loopback */ >+ /* Amp Indices: DAC = 0, mixer = 1 */ >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ >+ /* FIXME: use matrix-type input source selection */ >+ /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ >+ /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, >+ /* Input mixer2 */ >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, >+ /* Input mixer3 */ >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, >+ >+ { } >+}; >+ >+static struct hda_verb alc262_HP_BPC_init_verbs[] = { >+ /* >+ * Unmute ADC0-2 and set the default input to mic-in >+ */ >+ {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ >+ /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback >+ * mixer widget >+ * Note: PASD motherboards uses the Line In 2 as the input for >+ * front panel mic (mic 2) >+ */ >+ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, >+ >+ /* >+ * Set up output mixers (0x0c - 0x0e) >+ */ >+ /* set vol=0 to output mixers */ >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ >+ /* set up input amps for analog loopback */ >+ /* Amp Indices: DAC = 0, mixer = 1 */ >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ >+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ >+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, >+ >+ {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, >+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, >+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, >+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, >+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, >+ >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 }, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 }, >+ {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, >+ {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, >+ >+ >+ /* FIXME: use matrix-type input source selection */ >+ /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ >+ /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, >+ /* Input mixer2 */ >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, >+ /* Input mixer3 */ >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, >+ >+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, >+ >+ { } >+}; >+ >+static struct hda_verb alc262_HP_BPC_WildWest_init_verbs[] = { >+ /* >+ * Unmute ADC0-2 and set the default input to mic-in >+ */ >+ {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ >+ /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback >+ * mixer widget >+ * Note: PASD motherboards uses the Line In 2 as the input for front >+ * panel mic (mic 2) >+ */ >+ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, >+ /* >+ * Set up output mixers (0x0c - 0x0e) >+ */ >+ /* set vol=0 to output mixers */ >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ >+ /* set up input amps for analog loopback */ >+ /* Amp Indices: DAC = 0, mixer = 1 */ >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ >+ >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP */ >+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Mono */ >+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* rear MIC */ >+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* Line in */ >+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Front MIC */ >+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Line out */ >+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD in */ >+ >+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, >+ >+ {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ >+ /* {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 }, */ >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 }, >+ {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, >+ {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, >+ >+ /* FIXME: use matrix-type input source selection */ >+ /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ >+ /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, /*rear MIC*/ >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, /*Line in*/ >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, /*F MIC*/ >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, /*Front*/ >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, /*CD*/ >+ /* {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */ >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))}, /*HP*/ >+ /* Input mixer2 */ >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, >+ /* {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */ >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))}, >+ /* Input mixer3 */ >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, >+ /* {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */ >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))}, >+ >+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, >+ >+ { } >+}; >+ >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+#define alc262_loopbacks alc880_loopbacks >+#endif >+ >+/* pcm configuration: identiacal with ALC880 */ >+#define alc262_pcm_analog_playback alc880_pcm_analog_playback >+#define alc262_pcm_analog_capture alc880_pcm_analog_capture >+#define alc262_pcm_digital_playback alc880_pcm_digital_playback >+#define alc262_pcm_digital_capture alc880_pcm_digital_capture >+ >+/* >+ * BIOS auto configuration >+ */ >+static int alc262_parse_auto_config(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ int err; >+ static hda_nid_t alc262_ignore[] = { 0x1d, 0 }; >+ >+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, >+ alc262_ignore); >+ if (err < 0) >+ return err; >+ if (!spec->autocfg.line_outs) >+ return 0; /* can't find valid BIOS pin config */ >+ err = alc262_auto_create_multi_out_ctls(spec, &spec->autocfg); >+ if (err < 0) >+ return err; >+ err = alc262_auto_create_analog_input_ctls(spec, &spec->autocfg); >+ if (err < 0) >+ return err; >+ >+ spec->multiout.max_channels = spec->multiout.num_dacs * 2; >+ >+ if (spec->autocfg.dig_out_pin) >+ spec->multiout.dig_out_nid = ALC262_DIGOUT_NID; >+ if (spec->autocfg.dig_in_pin) >+ spec->dig_in_nid = ALC262_DIGIN_NID; >+ >+ if (spec->kctl_alloc) >+ spec->mixers[spec->num_mixers++] = spec->kctl_alloc; >+ >+ spec->init_verbs[spec->num_init_verbs++] = alc262_volume_init_verbs; >+ spec->num_mux_defs = 1; >+ spec->input_mux = &spec->private_imux; >+ >+ err = alc_auto_add_mic_boost(codec); >+ if (err < 0) >+ return err; >+ >+ return 1; >+} >+ >+#define alc262_auto_init_multi_out alc882_auto_init_multi_out >+#define alc262_auto_init_hp_out alc882_auto_init_hp_out >+#define alc262_auto_init_analog_input alc882_auto_init_analog_input >+ >+ >+/* init callback for auto-configuration model -- overriding the default init */ >+static void alc262_auto_init(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ alc262_auto_init_multi_out(codec); >+ alc262_auto_init_hp_out(codec); >+ alc262_auto_init_analog_input(codec); >+ if (spec->unsol_event) >+ alc_sku_automute(codec); >+} >+ >+/* >+ * configuration and preset >+ */ >+static const char *alc262_models[ALC262_MODEL_LAST] = { >+ [ALC262_BASIC] = "basic", >+ [ALC262_HIPPO] = "hippo", >+ [ALC262_HIPPO_1] = "hippo_1", >+ [ALC262_FUJITSU] = "fujitsu", >+ [ALC262_HP_BPC] = "hp-bpc", >+ [ALC262_HP_BPC_D7000_WL]= "hp-bpc-d7000", >+ [ALC262_HP_TC_T5735] = "hp-tc-t5735", >+ [ALC262_HP_RP5700] = "hp-rp5700", >+ [ALC262_BENQ_ED8] = "benq", >+ [ALC262_BENQ_T31] = "benq-t31", >+ [ALC262_SONY_ASSAMD] = "sony-assamd", >+ [ALC262_ULTRA] = "ultra", >+ [ALC262_LENOVO_3000] = "lenovo-3000", >+ [ALC262_AUTO] = "auto", >+}; >+ >+static struct snd_pci_quirk alc262_cfg_tbl[] = { >+ SND_PCI_QUIRK(0x1002, 0x437b, "Hippo", ALC262_HIPPO), >+ SND_PCI_QUIRK(0x103c, 0x12fe, "HP xw9400", ALC262_HP_BPC), >+ SND_PCI_QUIRK(0x103c, 0x12ff, "HP xw4550", ALC262_HP_BPC), >+ SND_PCI_QUIRK(0x103c, 0x1306, "HP xw8600", ALC262_HP_BPC), >+ SND_PCI_QUIRK(0x103c, 0x1307, "HP xw6600", ALC262_HP_BPC), >+ SND_PCI_QUIRK(0x103c, 0x1308, "HP xw4600", ALC262_HP_BPC), >+ SND_PCI_QUIRK(0x103c, 0x1309, "HP xw4*00", ALC262_HP_BPC), >+ SND_PCI_QUIRK(0x103c, 0x130a, "HP xw6*00", ALC262_HP_BPC), >+ SND_PCI_QUIRK(0x103c, 0x130b, "HP xw8*00", ALC262_HP_BPC), >+ SND_PCI_QUIRK(0x103c, 0x2800, "HP D7000", ALC262_HP_BPC_D7000_WL), >+ SND_PCI_QUIRK(0x103c, 0x2801, "HP D7000", ALC262_HP_BPC_D7000_WF), >+ SND_PCI_QUIRK(0x103c, 0x2802, "HP D7000", ALC262_HP_BPC_D7000_WL), >+ SND_PCI_QUIRK(0x103c, 0x2803, "HP D7000", ALC262_HP_BPC_D7000_WF), >+ SND_PCI_QUIRK(0x103c, 0x2804, "HP D7000", ALC262_HP_BPC_D7000_WL), >+ SND_PCI_QUIRK(0x103c, 0x2805, "HP D7000", ALC262_HP_BPC_D7000_WF), >+ SND_PCI_QUIRK(0x103c, 0x2806, "HP D7000", ALC262_HP_BPC_D7000_WL), >+ SND_PCI_QUIRK(0x103c, 0x2807, "HP D7000", ALC262_HP_BPC_D7000_WF), >+ SND_PCI_QUIRK(0x103c, 0x280c, "HP xw4400", ALC262_HP_BPC), >+ SND_PCI_QUIRK(0x103c, 0x3014, "HP xw6400", ALC262_HP_BPC), >+ SND_PCI_QUIRK(0x103c, 0x3015, "HP xw8400", ALC262_HP_BPC), >+ SND_PCI_QUIRK(0x103c, 0x302f, "HP Thin Client T5735", >+ ALC262_HP_TC_T5735), >+ SND_PCI_QUIRK(0x103c, 0x2817, "HP RP5700", ALC262_HP_RP5700), >+ SND_PCI_QUIRK(0x104d, 0x1f00, "Sony ASSAMD", ALC262_SONY_ASSAMD), >+ SND_PCI_QUIRK(0x104d, 0x8203, "Sony UX-90", ALC262_HIPPO), >+ SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD), >+ SND_PCI_QUIRK(0x104d, 0x900e, "Sony ASSAMD", ALC262_SONY_ASSAMD), >+ SND_PCI_QUIRK(0x104d, 0x9015, "Sony 0x9015", ALC262_SONY_ASSAMD), >+ SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU), >+ SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FUJITSU), >+ SND_PCI_QUIRK(0x144d, 0xc032, "Samsung Q1 Ultra", ALC262_ULTRA), >+ SND_PCI_QUIRK(0x144d, 0xc039, "Samsung Q1U EL", ALC262_ULTRA), >+ SND_PCI_QUIRK(0x17aa, 0x384e, "Lenovo 3000 y410", ALC262_LENOVO_3000), >+ SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_BENQ_ED8), >+ SND_PCI_QUIRK(0x17ff, 0x058d, "Benq T31-16", ALC262_BENQ_T31), >+ SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1), >+ {} >+}; >+ >+static struct alc_config_preset alc262_presets[] = { >+ [ALC262_BASIC] = { >+ .mixers = { alc262_base_mixer }, >+ .init_verbs = { alc262_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc262_dac_nids), >+ .dac_nids = alc262_dac_nids, >+ .hp_nid = 0x03, >+ .num_channel_mode = ARRAY_SIZE(alc262_modes), >+ .channel_mode = alc262_modes, >+ .input_mux = &alc262_capture_source, >+ }, >+ [ALC262_HIPPO] = { >+ .mixers = { alc262_base_mixer }, >+ .init_verbs = { alc262_init_verbs, alc262_hippo_unsol_verbs}, >+ .num_dacs = ARRAY_SIZE(alc262_dac_nids), >+ .dac_nids = alc262_dac_nids, >+ .hp_nid = 0x03, >+ .dig_out_nid = ALC262_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc262_modes), >+ .channel_mode = alc262_modes, >+ .input_mux = &alc262_capture_source, >+ .unsol_event = alc262_hippo_unsol_event, >+ .init_hook = alc262_hippo_automute, >+ }, >+ [ALC262_HIPPO_1] = { >+ .mixers = { alc262_hippo1_mixer }, >+ .init_verbs = { alc262_init_verbs, alc262_hippo1_unsol_verbs}, >+ .num_dacs = ARRAY_SIZE(alc262_dac_nids), >+ .dac_nids = alc262_dac_nids, >+ .hp_nid = 0x02, >+ .dig_out_nid = ALC262_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc262_modes), >+ .channel_mode = alc262_modes, >+ .input_mux = &alc262_capture_source, >+ .unsol_event = alc262_hippo1_unsol_event, >+ .init_hook = alc262_hippo1_automute, >+ }, >+ [ALC262_FUJITSU] = { >+ .mixers = { alc262_fujitsu_mixer }, >+ .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs, >+ alc262_fujitsu_unsol_verbs }, >+ .num_dacs = ARRAY_SIZE(alc262_dac_nids), >+ .dac_nids = alc262_dac_nids, >+ .hp_nid = 0x03, >+ .dig_out_nid = ALC262_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc262_modes), >+ .channel_mode = alc262_modes, >+ .input_mux = &alc262_fujitsu_capture_source, >+ .unsol_event = alc262_fujitsu_unsol_event, >+ .init_hook = alc262_fujitsu_init_hook, >+ }, >+ [ALC262_HP_BPC] = { >+ .mixers = { alc262_HP_BPC_mixer }, >+ .init_verbs = { alc262_HP_BPC_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc262_dac_nids), >+ .dac_nids = alc262_dac_nids, >+ .hp_nid = 0x03, >+ .num_channel_mode = ARRAY_SIZE(alc262_modes), >+ .channel_mode = alc262_modes, >+ .input_mux = &alc262_HP_capture_source, >+ .unsol_event = alc262_hp_bpc_unsol_event, >+ .init_hook = alc262_hp_bpc_automute, >+ }, >+ [ALC262_HP_BPC_D7000_WF] = { >+ .mixers = { alc262_HP_BPC_WildWest_mixer }, >+ .init_verbs = { alc262_HP_BPC_WildWest_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc262_dac_nids), >+ .dac_nids = alc262_dac_nids, >+ .hp_nid = 0x03, >+ .num_channel_mode = ARRAY_SIZE(alc262_modes), >+ .channel_mode = alc262_modes, >+ .input_mux = &alc262_HP_D7000_capture_source, >+ .unsol_event = alc262_hp_wildwest_unsol_event, >+ .init_hook = alc262_hp_wildwest_automute, >+ }, >+ [ALC262_HP_BPC_D7000_WL] = { >+ .mixers = { alc262_HP_BPC_WildWest_mixer, >+ alc262_HP_BPC_WildWest_option_mixer }, >+ .init_verbs = { alc262_HP_BPC_WildWest_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc262_dac_nids), >+ .dac_nids = alc262_dac_nids, >+ .hp_nid = 0x03, >+ .num_channel_mode = ARRAY_SIZE(alc262_modes), >+ .channel_mode = alc262_modes, >+ .input_mux = &alc262_HP_D7000_capture_source, >+ .unsol_event = alc262_hp_wildwest_unsol_event, >+ .init_hook = alc262_hp_wildwest_automute, >+ }, >+ [ALC262_HP_TC_T5735] = { >+ .mixers = { alc262_hp_t5735_mixer }, >+ .init_verbs = { alc262_init_verbs, alc262_hp_t5735_verbs }, >+ .num_dacs = ARRAY_SIZE(alc262_dac_nids), >+ .dac_nids = alc262_dac_nids, >+ .hp_nid = 0x03, >+ .num_channel_mode = ARRAY_SIZE(alc262_modes), >+ .channel_mode = alc262_modes, >+ .input_mux = &alc262_capture_source, >+ .unsol_event = alc262_hp_t5735_unsol_event, >+ .init_hook = alc262_hp_t5735_init_hook, >+ }, >+ [ALC262_HP_RP5700] = { >+ .mixers = { alc262_hp_rp5700_mixer }, >+ .init_verbs = { alc262_init_verbs, alc262_hp_rp5700_verbs }, >+ .num_dacs = ARRAY_SIZE(alc262_dac_nids), >+ .dac_nids = alc262_dac_nids, >+ .num_channel_mode = ARRAY_SIZE(alc262_modes), >+ .channel_mode = alc262_modes, >+ .input_mux = &alc262_hp_rp5700_capture_source, >+ }, >+ [ALC262_BENQ_ED8] = { >+ .mixers = { alc262_base_mixer }, >+ .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs }, >+ .num_dacs = ARRAY_SIZE(alc262_dac_nids), >+ .dac_nids = alc262_dac_nids, >+ .hp_nid = 0x03, >+ .num_channel_mode = ARRAY_SIZE(alc262_modes), >+ .channel_mode = alc262_modes, >+ .input_mux = &alc262_capture_source, >+ }, >+ [ALC262_SONY_ASSAMD] = { >+ .mixers = { alc262_sony_mixer }, >+ .init_verbs = { alc262_init_verbs, alc262_sony_unsol_verbs}, >+ .num_dacs = ARRAY_SIZE(alc262_dac_nids), >+ .dac_nids = alc262_dac_nids, >+ .hp_nid = 0x02, >+ .num_channel_mode = ARRAY_SIZE(alc262_modes), >+ .channel_mode = alc262_modes, >+ .input_mux = &alc262_capture_source, >+ .unsol_event = alc262_hippo_unsol_event, >+ .init_hook = alc262_hippo_automute, >+ }, >+ [ALC262_BENQ_T31] = { >+ .mixers = { alc262_benq_t31_mixer }, >+ .init_verbs = { alc262_init_verbs, alc262_benq_t31_EAPD_verbs, alc262_hippo_unsol_verbs }, >+ .num_dacs = ARRAY_SIZE(alc262_dac_nids), >+ .dac_nids = alc262_dac_nids, >+ .hp_nid = 0x03, >+ .num_channel_mode = ARRAY_SIZE(alc262_modes), >+ .channel_mode = alc262_modes, >+ .input_mux = &alc262_capture_source, >+ .unsol_event = alc262_hippo_unsol_event, >+ .init_hook = alc262_hippo_automute, >+ }, >+ [ALC262_ULTRA] = { >+ .mixers = { alc262_ultra_mixer, alc262_ultra_capture_mixer }, >+ .init_verbs = { alc262_ultra_verbs }, >+ .num_dacs = ARRAY_SIZE(alc262_dac_nids), >+ .dac_nids = alc262_dac_nids, >+ .num_channel_mode = ARRAY_SIZE(alc262_modes), >+ .channel_mode = alc262_modes, >+ .input_mux = &alc262_ultra_capture_source, >+ .adc_nids = alc262_adc_nids, /* ADC0 */ >+ .capsrc_nids = alc262_capsrc_nids, >+ .num_adc_nids = 1, /* single ADC */ >+ .unsol_event = alc262_ultra_unsol_event, >+ .init_hook = alc262_ultra_automute, >+ }, >+ [ALC262_LENOVO_3000] = { >+ .mixers = { alc262_lenovo_3000_mixer }, >+ .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs, >+ alc262_lenovo_3000_unsol_verbs }, >+ .num_dacs = ARRAY_SIZE(alc262_dac_nids), >+ .dac_nids = alc262_dac_nids, >+ .hp_nid = 0x03, >+ .dig_out_nid = ALC262_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc262_modes), >+ .channel_mode = alc262_modes, >+ .input_mux = &alc262_fujitsu_capture_source, >+ .unsol_event = alc262_lenovo_3000_unsol_event, >+ }, >+}; >+ >+static int patch_alc262(struct hda_codec *codec) >+{ >+ struct alc_spec *spec; >+ int board_config; >+ int err; >+ >+ spec = kzalloc(sizeof(*spec), GFP_KERNEL); >+ if (spec == NULL) >+ return -ENOMEM; >+ >+ codec->spec = spec; >+#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); >+ >+ board_config = snd_hda_check_board_config(codec, ALC262_MODEL_LAST, >+ alc262_models, >+ alc262_cfg_tbl); >+ >+ if (board_config < 0) { >+ printk(KERN_INFO "hda_codec: Unknown model for ALC262, " >+ "trying auto-probe from BIOS...\n"); >+ board_config = ALC262_AUTO; >+ } >+ >+ if (board_config == ALC262_AUTO) { >+ /* automatic parse from the BIOS config */ >+ err = alc262_parse_auto_config(codec); >+ if (err < 0) { >+ alc_free(codec); >+ return err; >+ } else if (!err) { >+ printk(KERN_INFO >+ "hda_codec: Cannot set up configuration " >+ "from BIOS. Using base mode...\n"); >+ board_config = ALC262_BASIC; >+ } >+ } >+ >+ if (board_config != ALC262_AUTO) >+ setup_preset(spec, &alc262_presets[board_config]); >+ >+ spec->stream_name_analog = "ALC262 Analog"; >+ spec->stream_analog_playback = &alc262_pcm_analog_playback; >+ spec->stream_analog_capture = &alc262_pcm_analog_capture; >+ >+ spec->stream_name_digital = "ALC262 Digital"; >+ spec->stream_digital_playback = &alc262_pcm_digital_playback; >+ spec->stream_digital_capture = &alc262_pcm_digital_capture; >+ >+ if (!spec->adc_nids && spec->input_mux) { >+ /* check whether NID 0x07 is valid */ >+ unsigned int wcap = get_wcaps(codec, 0x07); >+ >+ /* get type */ >+ wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; >+ if (wcap != AC_WID_AUD_IN) { >+ spec->adc_nids = alc262_adc_nids_alt; >+ spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids_alt); >+ spec->capsrc_nids = alc262_capsrc_nids_alt; >+ spec->mixers[spec->num_mixers] = >+ alc262_capture_alt_mixer; >+ spec->num_mixers++; >+ } else { >+ spec->adc_nids = alc262_adc_nids; >+ spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids); >+ spec->capsrc_nids = alc262_capsrc_nids; >+ spec->mixers[spec->num_mixers] = alc262_capture_mixer; >+ spec->num_mixers++; >+ } >+ } >+ >+ spec->vmaster_nid = 0x0c; >+ >+ codec->patch_ops = alc_patch_ops; >+ if (board_config == ALC262_AUTO) >+ spec->init_hook = alc262_auto_init; >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+ if (!spec->loopback.amplist) >+ spec->loopback.amplist = alc262_loopbacks; >+#endif >+ >+ return 0; >+} >+ >+/* >+ * ALC268 channel source setting (2 channel) >+ */ >+#define ALC268_DIGOUT_NID ALC880_DIGOUT_NID >+#define alc268_modes alc260_modes >+ >+static hda_nid_t alc268_dac_nids[2] = { >+ /* front, hp */ >+ 0x02, 0x03 >+}; >+ >+static hda_nid_t alc268_adc_nids[2] = { >+ /* ADC0-1 */ >+ 0x08, 0x07 >+}; >+ >+static hda_nid_t alc268_adc_nids_alt[1] = { >+ /* ADC0 */ >+ 0x08 >+}; >+ >+static hda_nid_t alc268_capsrc_nids[2] = { 0x23, 0x24 }; >+ >+static struct snd_kcontrol_new alc268_base_mixer[] = { >+ /* output mixer control */ >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line In Boost", 0x1a, 0, HDA_INPUT), >+ { } >+}; >+ >+/* bind Beep switches of both NID 0x0f and 0x10 */ >+static struct hda_bind_ctls alc268_bind_beep_sw = { >+ .ops = &snd_hda_bind_sw, >+ .values = { >+ HDA_COMPOSE_AMP_VAL(0x0f, 3, 1, HDA_INPUT), >+ HDA_COMPOSE_AMP_VAL(0x10, 3, 1, HDA_INPUT), >+ 0 >+ }, >+}; >+ >+static struct snd_kcontrol_new alc268_beep_mixer[] = { >+ HDA_CODEC_VOLUME("Beep Playback Volume", 0x1d, 0x0, HDA_INPUT), >+ HDA_BIND_SW("Beep Playback Switch", &alc268_bind_beep_sw), >+ { } >+}; >+ >+static struct hda_verb alc268_eapd_verbs[] = { >+ {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, >+ {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, >+ { } >+}; >+ >+/* Toshiba specific */ >+#define alc268_toshiba_automute alc262_hippo_automute >+ >+static struct hda_verb alc268_toshiba_verbs[] = { >+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, >+ { } /* end */ >+}; >+ >+/* Acer specific */ >+/* bind volumes of both NID 0x02 and 0x03 */ >+static struct hda_bind_ctls alc268_acer_bind_master_vol = { >+ .ops = &snd_hda_bind_vol, >+ .values = { >+ HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT), >+ HDA_COMPOSE_AMP_VAL(0x03, 3, 0, HDA_OUTPUT), >+ 0 >+ }, >+}; >+ >+/* mute/unmute internal speaker according to the hp jack and mute state */ >+static void alc268_acer_automute(struct hda_codec *codec, int force) >+{ >+ struct alc_spec *spec = codec->spec; >+ unsigned int mute; >+ >+ if (force || !spec->sense_updated) { >+ unsigned int present; >+ present = snd_hda_codec_read(codec, 0x14, 0, >+ AC_VERB_GET_PIN_SENSE, 0); >+ spec->jack_present = (present & 0x80000000) != 0; >+ spec->sense_updated = 1; >+ } >+ if (spec->jack_present) >+ mute = HDA_AMP_MUTE; /* mute internal speaker */ >+ else /* unmute internal speaker if necessary */ >+ mute = snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0); >+ snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, mute); >+} >+ >+ >+/* bind hp and internal speaker mute (with plug check) */ >+static int alc268_acer_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, 0x14, 0, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, >+ valp[0] ? 0 : HDA_AMP_MUTE); >+ change |= snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, >+ valp[1] ? 0 : HDA_AMP_MUTE); >+ if (change) >+ alc268_acer_automute(codec, 0); >+ return change; >+} >+ >+static struct snd_kcontrol_new alc268_acer_mixer[] = { >+ /* output mixer control */ >+ HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Master Playback Switch", >+ .info = snd_hda_mixer_amp_switch_info, >+ .get = snd_hda_mixer_amp_switch_get, >+ .put = alc268_acer_master_sw_put, >+ .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), >+ }, >+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Internal Mic Boost", 0x19, 0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line In Boost", 0x1a, 0, HDA_INPUT), >+ { } >+}; >+ >+static struct hda_verb alc268_acer_verbs[] = { >+ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* internal dmic? */ >+ {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ >+ {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, >+ { } >+}; >+ >+/* unsolicited event for HP jack sensing */ >+static void alc268_toshiba_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ if ((res >> 26) != ALC880_HP_EVENT) >+ return; >+ alc268_toshiba_automute(codec); >+} >+ >+static void alc268_acer_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ if ((res >> 26) != ALC880_HP_EVENT) >+ return; >+ alc268_acer_automute(codec, 1); >+} >+ >+static void alc268_acer_init_hook(struct hda_codec *codec) >+{ >+ alc268_acer_automute(codec, 1); >+} >+ >+static struct snd_kcontrol_new alc268_dell_mixer[] = { >+ /* output mixer control */ >+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Internal Mic Boost", 0x19, 0, HDA_INPUT), >+ { } >+}; >+ >+static struct hda_verb alc268_dell_verbs[] = { >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, >+ { } >+}; >+ >+/* mute/unmute internal speaker according to the hp jack and mute state */ >+static void alc268_dell_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ unsigned int mute; >+ >+ present = snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_PIN_SENSE, 0); >+ if (present & 0x80000000) >+ mute = HDA_AMP_MUTE; >+ else >+ mute = snd_hda_codec_amp_read(codec, 0x15, 0, HDA_OUTPUT, 0); >+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, mute); >+} >+ >+static void alc268_dell_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ if ((res >> 26) != ALC880_HP_EVENT) >+ return; >+ alc268_dell_automute(codec); >+} >+ >+#define alc268_dell_init_hook alc268_dell_automute >+ >+static struct snd_kcontrol_new alc267_quanta_il1_mixer[] = { >+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x2, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Mic Capture Volume", 0x23, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Mic Capture Switch", 0x23, 2, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Ext Mic Boost", 0x18, 0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT), >+ { } >+}; >+ >+static struct hda_verb alc267_quanta_il1_verbs[] = { >+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, >+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, >+ { } >+}; >+ >+static void alc267_quanta_il1_hp_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ >+ present = snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_PIN_SENSE, 0) >+ & AC_PINSENSE_PRESENCE; >+ snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, >+ present ? 0 : PIN_OUT); >+} >+ >+static void alc267_quanta_il1_mic_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ >+ present = snd_hda_codec_read(codec, 0x18, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ snd_hda_codec_write(codec, 0x23, 0, >+ AC_VERB_SET_CONNECT_SEL, >+ present ? 0x00 : 0x01); >+} >+ >+static void alc267_quanta_il1_automute(struct hda_codec *codec) >+{ >+ alc267_quanta_il1_hp_automute(codec); >+ alc267_quanta_il1_mic_automute(codec); >+} >+ >+static void alc267_quanta_il1_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ switch (res >> 26) { >+ case ALC880_HP_EVENT: >+ alc267_quanta_il1_hp_automute(codec); >+ break; >+ case ALC880_MIC_EVENT: >+ alc267_quanta_il1_mic_automute(codec); >+ break; >+ } >+} >+ >+/* >+ * generic initialization of ADC, input mixers and output mixers >+ */ >+static struct hda_verb alc268_base_init_verbs[] = { >+ /* Unmute DAC0-1 and set vol = 0 */ >+ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ >+ /* >+ * Set up output mixers (0x0c - 0x0e) >+ */ >+ /* set vol=0 to output mixers */ >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0e, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, >+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, >+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, >+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, >+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, >+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, >+ {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, >+ >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ >+ /* set PCBEEP vol = 0, mute connections */ >+ {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ >+ /* Unmute Selector 23h,24h and set the default input to mic-in */ >+ >+ {0x23, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x24, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ >+ { } >+}; >+ >+/* >+ * generic initialization of ADC, input mixers and output mixers >+ */ >+static struct hda_verb alc268_volume_init_verbs[] = { >+ /* set output DAC */ >+ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ >+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, >+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, >+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, >+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, >+ {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, >+ >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ >+ /* set PCBEEP vol = 0, mute connections */ >+ {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ >+ { } >+}; >+ >+#define alc268_mux_enum_info alc_mux_enum_info >+#define alc268_mux_enum_get alc_mux_enum_get >+#define alc268_mux_enum_put alc_mux_enum_put >+ >+static struct snd_kcontrol_new alc268_capture_alt_mixer[] = { >+ HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ /* The multiple "Capture Source" controls confuse alsamixer >+ * So call somewhat different.. >+ */ >+ /* .name = "Capture Source", */ >+ .name = "Input Source", >+ .count = 1, >+ .info = alc268_mux_enum_info, >+ .get = alc268_mux_enum_get, >+ .put = alc268_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc268_capture_mixer[] = { >+ HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x24, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x24, 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 = alc268_mux_enum_info, >+ .get = alc268_mux_enum_get, >+ .put = alc268_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+static struct hda_input_mux alc268_capture_source = { >+ .num_items = 4, >+ .items = { >+ { "Mic", 0x0 }, >+ { "Front Mic", 0x1 }, >+ { "Line", 0x2 }, >+ { "CD", 0x3 }, >+ }, >+}; >+ >+static struct hda_input_mux alc268_acer_capture_source = { >+ .num_items = 3, >+ .items = { >+ { "Mic", 0x0 }, >+ { "Internal Mic", 0x6 }, >+ { "Line", 0x2 }, >+ }, >+}; >+ >+#ifdef CONFIG_SND_DEBUG >+static struct snd_kcontrol_new alc268_test_mixer[] = { >+ /* Volume widgets */ >+ HDA_CODEC_VOLUME("LOUT1 Playback Volume", 0x02, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("LOUT2 Playback Volume", 0x03, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE_MONO("Mono sum Playback Switch", 0x0e, 1, 2, HDA_INPUT), >+ HDA_BIND_MUTE("LINE-OUT sum Playback Switch", 0x0f, 2, HDA_INPUT), >+ HDA_BIND_MUTE("HP-OUT sum Playback Switch", 0x10, 2, HDA_INPUT), >+ HDA_BIND_MUTE("LINE-OUT Playback Switch", 0x14, 2, HDA_OUTPUT), >+ HDA_BIND_MUTE("HP-OUT Playback Switch", 0x15, 2, HDA_OUTPUT), >+ HDA_BIND_MUTE("Mono Playback Switch", 0x16, 2, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("MIC1 Capture Volume", 0x18, 0x0, HDA_INPUT), >+ HDA_BIND_MUTE("MIC1 Capture Switch", 0x18, 2, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("MIC2 Capture Volume", 0x19, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("LINE1 Capture Volume", 0x1a, 0x0, HDA_INPUT), >+ HDA_BIND_MUTE("LINE1 Capture Switch", 0x1a, 2, HDA_OUTPUT), >+ /* The below appears problematic on some hardwares */ >+ /*HDA_CODEC_VOLUME("PCBEEP Playback Volume", 0x1d, 0x0, HDA_INPUT),*/ >+ HDA_CODEC_VOLUME("PCM-IN1 Capture Volume", 0x23, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("PCM-IN1 Capture Switch", 0x23, 2, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("PCM-IN2 Capture Volume", 0x24, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("PCM-IN2 Capture Switch", 0x24, 2, HDA_OUTPUT), >+ >+ /* Modes for retasking pin widgets */ >+ ALC_PIN_MODE("LINE-OUT pin mode", 0x14, ALC_PIN_DIR_INOUT), >+ ALC_PIN_MODE("HP-OUT pin mode", 0x15, ALC_PIN_DIR_INOUT), >+ ALC_PIN_MODE("MIC1 pin mode", 0x18, ALC_PIN_DIR_INOUT), >+ ALC_PIN_MODE("LINE1 pin mode", 0x1a, ALC_PIN_DIR_INOUT), >+ >+ /* Controls for GPIO pins, assuming they are configured as outputs */ >+ ALC_GPIO_DATA_SWITCH("GPIO pin 0", 0x01, 0x01), >+ ALC_GPIO_DATA_SWITCH("GPIO pin 1", 0x01, 0x02), >+ ALC_GPIO_DATA_SWITCH("GPIO pin 2", 0x01, 0x04), >+ ALC_GPIO_DATA_SWITCH("GPIO pin 3", 0x01, 0x08), >+ >+ /* Switches to allow the digital SPDIF output pin to be enabled. >+ * The ALC268 does not have an SPDIF input. >+ */ >+ ALC_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x06, 0x01), >+ >+ /* A switch allowing EAPD to be enabled. Some laptops seem to use >+ * this output to turn on an external amplifier. >+ */ >+ ALC_EAPD_CTRL_SWITCH("LINE-OUT EAPD Enable Switch", 0x0f, 0x02), >+ ALC_EAPD_CTRL_SWITCH("HP-OUT EAPD Enable Switch", 0x10, 0x02), >+ >+ { } /* end */ >+}; >+#endif >+ >+/* create input playback/capture controls for the given pin */ >+static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid, >+ const char *ctlname, int idx) >+{ >+ char name[32]; >+ int err; >+ >+ sprintf(name, "%s Playback Volume", ctlname); >+ if (nid == 0x14) { >+ err = add_control(spec, ALC_CTL_WIDGET_VOL, name, >+ HDA_COMPOSE_AMP_VAL(0x02, 3, idx, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ } else if (nid == 0x15) { >+ err = add_control(spec, ALC_CTL_WIDGET_VOL, name, >+ HDA_COMPOSE_AMP_VAL(0x03, 3, idx, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ } else >+ return -1; >+ sprintf(name, "%s Playback Switch", ctlname); >+ err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, >+ HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ return 0; >+} >+ >+/* add playback controls from the parsed DAC table */ >+static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec, >+ const struct auto_pin_cfg *cfg) >+{ >+ hda_nid_t nid; >+ int err; >+ >+ spec->multiout.num_dacs = 2; /* only use one dac */ >+ spec->multiout.dac_nids = spec->private_dac_nids; >+ spec->multiout.dac_nids[0] = 2; >+ spec->multiout.dac_nids[1] = 3; >+ >+ nid = cfg->line_out_pins[0]; >+ if (nid) >+ alc268_new_analog_output(spec, nid, "Front", 0); >+ >+ nid = cfg->speaker_pins[0]; >+ if (nid == 0x1d) { >+ err = add_control(spec, ALC_CTL_WIDGET_VOL, >+ "Speaker Playback Volume", >+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT)); >+ if (err < 0) >+ return err; >+ } >+ nid = cfg->hp_pins[0]; >+ if (nid) >+ alc268_new_analog_output(spec, nid, "Headphone", 0); >+ >+ nid = cfg->line_out_pins[1] | cfg->line_out_pins[2]; >+ if (nid == 0x16) { >+ err = add_control(spec, ALC_CTL_WIDGET_MUTE, >+ "Mono Playback Switch", >+ HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_INPUT)); >+ if (err < 0) >+ return err; >+ } >+ return 0; >+} >+ >+/* create playback/capture controls for input pins */ >+static int alc268_auto_create_analog_input_ctls(struct alc_spec *spec, >+ const struct auto_pin_cfg *cfg) >+{ >+ struct hda_input_mux *imux = &spec->private_imux; >+ int i, idx1; >+ >+ for (i = 0; i < AUTO_PIN_LAST; i++) { >+ switch(cfg->input_pins[i]) { >+ case 0x18: >+ idx1 = 0; /* Mic 1 */ >+ break; >+ case 0x19: >+ idx1 = 1; /* Mic 2 */ >+ break; >+ case 0x1a: >+ idx1 = 2; /* Line In */ >+ break; >+ case 0x1c: >+ idx1 = 3; /* CD */ >+ break; >+ case 0x12: >+ case 0x13: >+ idx1 = 6; /* digital mics */ >+ break; >+ default: >+ continue; >+ } >+ imux->items[imux->num_items].label = auto_pin_cfg_labels[i]; >+ imux->items[imux->num_items].index = idx1; >+ imux->num_items++; >+ } >+ return 0; >+} >+ >+static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ hda_nid_t speaker_nid = spec->autocfg.speaker_pins[0]; >+ hda_nid_t hp_nid = spec->autocfg.hp_pins[0]; >+ hda_nid_t line_nid = spec->autocfg.line_out_pins[0]; >+ unsigned int dac_vol1, dac_vol2; >+ >+ if (speaker_nid) { >+ snd_hda_codec_write(codec, speaker_nid, 0, >+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); >+ snd_hda_codec_write(codec, 0x0f, 0, >+ AC_VERB_SET_AMP_GAIN_MUTE, >+ AMP_IN_UNMUTE(1)); >+ snd_hda_codec_write(codec, 0x10, 0, >+ AC_VERB_SET_AMP_GAIN_MUTE, >+ AMP_IN_UNMUTE(1)); >+ } else { >+ snd_hda_codec_write(codec, 0x0f, 0, >+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)); >+ snd_hda_codec_write(codec, 0x10, 0, >+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)); >+ } >+ >+ dac_vol1 = dac_vol2 = 0xb000 | 0x40; /* set max volume */ >+ if (line_nid == 0x14) >+ dac_vol2 = AMP_OUT_ZERO; >+ else if (line_nid == 0x15) >+ dac_vol1 = AMP_OUT_ZERO; >+ if (hp_nid == 0x14) >+ dac_vol2 = AMP_OUT_ZERO; >+ else if (hp_nid == 0x15) >+ dac_vol1 = AMP_OUT_ZERO; >+ if (line_nid != 0x16 || hp_nid != 0x16 || >+ spec->autocfg.line_out_pins[1] != 0x16 || >+ spec->autocfg.line_out_pins[2] != 0x16) >+ dac_vol1 = dac_vol2 = AMP_OUT_ZERO; >+ >+ snd_hda_codec_write(codec, 0x02, 0, >+ AC_VERB_SET_AMP_GAIN_MUTE, dac_vol1); >+ snd_hda_codec_write(codec, 0x03, 0, >+ AC_VERB_SET_AMP_GAIN_MUTE, dac_vol2); >+} >+ >+/* pcm configuration: identiacal with ALC880 */ >+#define alc268_pcm_analog_playback alc880_pcm_analog_playback >+#define alc268_pcm_analog_capture alc880_pcm_analog_capture >+#define alc268_pcm_analog_alt_capture alc880_pcm_analog_alt_capture >+#define alc268_pcm_digital_playback alc880_pcm_digital_playback >+ >+/* >+ * BIOS auto configuration >+ */ >+static int alc268_parse_auto_config(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ int err; >+ static hda_nid_t alc268_ignore[] = { 0 }; >+ >+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, >+ alc268_ignore); >+ if (err < 0) >+ return err; >+ if (!spec->autocfg.line_outs) >+ return 0; /* can't find valid BIOS pin config */ >+ >+ err = alc268_auto_create_multi_out_ctls(spec, &spec->autocfg); >+ if (err < 0) >+ return err; >+ err = alc268_auto_create_analog_input_ctls(spec, &spec->autocfg); >+ if (err < 0) >+ return err; >+ >+ spec->multiout.max_channels = 2; >+ >+ /* digital only support output */ >+ if (spec->autocfg.dig_out_pin) >+ spec->multiout.dig_out_nid = ALC268_DIGOUT_NID; >+ >+ if (spec->kctl_alloc) >+ spec->mixers[spec->num_mixers++] = spec->kctl_alloc; >+ >+ if (spec->autocfg.speaker_pins[0] != 0x1d) >+ spec->mixers[spec->num_mixers++] = alc268_beep_mixer; >+ >+ spec->init_verbs[spec->num_init_verbs++] = alc268_volume_init_verbs; >+ spec->num_mux_defs = 1; >+ spec->input_mux = &spec->private_imux; >+ >+ err = alc_auto_add_mic_boost(codec); >+ if (err < 0) >+ return err; >+ >+ return 1; >+} >+ >+#define alc268_auto_init_multi_out alc882_auto_init_multi_out >+#define alc268_auto_init_hp_out alc882_auto_init_hp_out >+#define alc268_auto_init_analog_input alc882_auto_init_analog_input >+ >+/* init callback for auto-configuration model -- overriding the default init */ >+static void alc268_auto_init(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ alc268_auto_init_multi_out(codec); >+ alc268_auto_init_hp_out(codec); >+ alc268_auto_init_mono_speaker_out(codec); >+ alc268_auto_init_analog_input(codec); >+ if (spec->unsol_event) >+ alc_sku_automute(codec); >+} >+ >+/* >+ * configuration and preset >+ */ >+static const char *alc268_models[ALC268_MODEL_LAST] = { >+ [ALC267_QUANTA_IL1] = "quanta-il1", >+ [ALC268_3ST] = "3stack", >+ [ALC268_TOSHIBA] = "toshiba", >+ [ALC268_ACER] = "acer", >+ [ALC268_DELL] = "dell", >+ [ALC268_ZEPTO] = "zepto", >+#ifdef CONFIG_SND_DEBUG >+ [ALC268_TEST] = "test", >+#endif >+ [ALC268_AUTO] = "auto", >+}; >+ >+static struct snd_pci_quirk alc268_cfg_tbl[] = { >+ SND_PCI_QUIRK(0x1025, 0x011e, "Acer Aspire 5720z", ALC268_ACER), >+ SND_PCI_QUIRK(0x1025, 0x0126, "Acer", ALC268_ACER), >+ SND_PCI_QUIRK(0x1025, 0x012e, "Acer Aspire 5310", ALC268_ACER), >+ SND_PCI_QUIRK(0x1025, 0x0130, "Acer Extensa 5210", ALC268_ACER), >+ SND_PCI_QUIRK(0x1025, 0x0136, "Acer Aspire 5315", ALC268_ACER), >+ SND_PCI_QUIRK(0x1028, 0x0253, "Dell OEM", ALC268_DELL), >+ SND_PCI_QUIRK(0x103c, 0x30cc, "TOSHIBA", ALC268_TOSHIBA), >+ SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC268_3ST), >+ SND_PCI_QUIRK(0x1179, 0xff10, "TOSHIBA A205", ALC268_TOSHIBA), >+ SND_PCI_QUIRK(0x1179, 0xff50, "TOSHIBA A305", ALC268_TOSHIBA), >+ SND_PCI_QUIRK(0x14c0, 0x0025, "COMPAL IFL90/JFL-92", ALC268_TOSHIBA), >+ SND_PCI_QUIRK(0x152d, 0x0763, "Diverse (CPR2000)", ALC268_ACER), >+ SND_PCI_QUIRK(0x152d, 0x0771, "Quanta IL1", ALC267_QUANTA_IL1), >+ SND_PCI_QUIRK(0x1170, 0x0040, "ZEPTO", ALC268_ZEPTO), >+ {} >+}; >+ >+static struct alc_config_preset alc268_presets[] = { >+ [ALC267_QUANTA_IL1] = { >+ .mixers = { alc267_quanta_il1_mixer }, >+ .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, >+ alc267_quanta_il1_verbs }, >+ .num_dacs = ARRAY_SIZE(alc268_dac_nids), >+ .dac_nids = alc268_dac_nids, >+ .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), >+ .adc_nids = alc268_adc_nids_alt, >+ .hp_nid = 0x03, >+ .num_channel_mode = ARRAY_SIZE(alc268_modes), >+ .channel_mode = alc268_modes, >+ .input_mux = &alc268_capture_source, >+ .unsol_event = alc267_quanta_il1_unsol_event, >+ .init_hook = alc267_quanta_il1_automute, >+ }, >+ [ALC268_3ST] = { >+ .mixers = { alc268_base_mixer, alc268_capture_alt_mixer, >+ alc268_beep_mixer }, >+ .init_verbs = { alc268_base_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc268_dac_nids), >+ .dac_nids = alc268_dac_nids, >+ .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), >+ .adc_nids = alc268_adc_nids_alt, >+ .capsrc_nids = alc268_capsrc_nids, >+ .hp_nid = 0x03, >+ .dig_out_nid = ALC268_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc268_modes), >+ .channel_mode = alc268_modes, >+ .input_mux = &alc268_capture_source, >+ }, >+ [ALC268_TOSHIBA] = { >+ .mixers = { alc268_base_mixer, alc268_capture_alt_mixer, >+ alc268_beep_mixer }, >+ .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, >+ alc268_toshiba_verbs }, >+ .num_dacs = ARRAY_SIZE(alc268_dac_nids), >+ .dac_nids = alc268_dac_nids, >+ .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), >+ .adc_nids = alc268_adc_nids_alt, >+ .capsrc_nids = alc268_capsrc_nids, >+ .hp_nid = 0x03, >+ .num_channel_mode = ARRAY_SIZE(alc268_modes), >+ .channel_mode = alc268_modes, >+ .input_mux = &alc268_capture_source, >+ .unsol_event = alc268_toshiba_unsol_event, >+ .init_hook = alc268_toshiba_automute, >+ }, >+ [ALC268_ACER] = { >+ .mixers = { alc268_acer_mixer, alc268_capture_alt_mixer, >+ alc268_beep_mixer }, >+ .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, >+ alc268_acer_verbs }, >+ .num_dacs = ARRAY_SIZE(alc268_dac_nids), >+ .dac_nids = alc268_dac_nids, >+ .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), >+ .adc_nids = alc268_adc_nids_alt, >+ .capsrc_nids = alc268_capsrc_nids, >+ .hp_nid = 0x02, >+ .num_channel_mode = ARRAY_SIZE(alc268_modes), >+ .channel_mode = alc268_modes, >+ .input_mux = &alc268_acer_capture_source, >+ .unsol_event = alc268_acer_unsol_event, >+ .init_hook = alc268_acer_init_hook, >+ }, >+ [ALC268_DELL] = { >+ .mixers = { alc268_dell_mixer, alc268_beep_mixer }, >+ .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, >+ alc268_dell_verbs }, >+ .num_dacs = ARRAY_SIZE(alc268_dac_nids), >+ .dac_nids = alc268_dac_nids, >+ .hp_nid = 0x02, >+ .num_channel_mode = ARRAY_SIZE(alc268_modes), >+ .channel_mode = alc268_modes, >+ .unsol_event = alc268_dell_unsol_event, >+ .init_hook = alc268_dell_init_hook, >+ .input_mux = &alc268_capture_source, >+ }, >+ [ALC268_ZEPTO] = { >+ .mixers = { alc268_base_mixer, alc268_capture_alt_mixer, >+ alc268_beep_mixer }, >+ .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, >+ alc268_toshiba_verbs }, >+ .num_dacs = ARRAY_SIZE(alc268_dac_nids), >+ .dac_nids = alc268_dac_nids, >+ .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), >+ .adc_nids = alc268_adc_nids_alt, >+ .capsrc_nids = alc268_capsrc_nids, >+ .hp_nid = 0x03, >+ .dig_out_nid = ALC268_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc268_modes), >+ .channel_mode = alc268_modes, >+ .input_mux = &alc268_capture_source, >+ .unsol_event = alc268_toshiba_unsol_event, >+ .init_hook = alc268_toshiba_automute >+ }, >+#ifdef CONFIG_SND_DEBUG >+ [ALC268_TEST] = { >+ .mixers = { alc268_test_mixer, alc268_capture_mixer }, >+ .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, >+ alc268_volume_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc268_dac_nids), >+ .dac_nids = alc268_dac_nids, >+ .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), >+ .adc_nids = alc268_adc_nids_alt, >+ .capsrc_nids = alc268_capsrc_nids, >+ .hp_nid = 0x03, >+ .dig_out_nid = ALC268_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc268_modes), >+ .channel_mode = alc268_modes, >+ .input_mux = &alc268_capture_source, >+ }, >+#endif >+}; >+ >+static int patch_alc268(struct hda_codec *codec) >+{ >+ struct alc_spec *spec; >+ int board_config; >+ int err; >+ >+ spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); >+ if (spec == NULL) >+ return -ENOMEM; >+ >+ codec->spec = spec; >+ >+ board_config = snd_hda_check_board_config(codec, ALC268_MODEL_LAST, >+ alc268_models, >+ alc268_cfg_tbl); >+ >+ if (board_config < 0 || board_config >= ALC268_MODEL_LAST) { >+ printk(KERN_INFO "hda_codec: Unknown model for ALC268, " >+ "trying auto-probe from BIOS...\n"); >+ board_config = ALC268_AUTO; >+ } >+ >+ if (board_config == ALC268_AUTO) { >+ /* automatic parse from the BIOS config */ >+ err = alc268_parse_auto_config(codec); >+ if (err < 0) { >+ alc_free(codec); >+ return err; >+ } else if (!err) { >+ printk(KERN_INFO >+ "hda_codec: Cannot set up configuration " >+ "from BIOS. Using base mode...\n"); >+ board_config = ALC268_3ST; >+ } >+ } >+ >+ if (board_config != ALC268_AUTO) >+ setup_preset(spec, &alc268_presets[board_config]); >+ >+ if (codec->vendor_id == 0x10ec0267) { >+ spec->stream_name_analog = "ALC267 Analog"; >+ spec->stream_name_digital = "ALC267 Digital"; >+ } else { >+ spec->stream_name_analog = "ALC268 Analog"; >+ spec->stream_name_digital = "ALC268 Digital"; >+ } >+ >+ spec->stream_analog_playback = &alc268_pcm_analog_playback; >+ spec->stream_analog_capture = &alc268_pcm_analog_capture; >+ spec->stream_analog_alt_capture = &alc268_pcm_analog_alt_capture; >+ >+ spec->stream_digital_playback = &alc268_pcm_digital_playback; >+ >+ if (!query_amp_caps(codec, 0x1d, HDA_INPUT)) >+ /* override the amp caps for beep generator */ >+ snd_hda_override_amp_caps(codec, 0x1d, HDA_INPUT, >+ (0x0c << AC_AMPCAP_OFFSET_SHIFT) | >+ (0x0c << AC_AMPCAP_NUM_STEPS_SHIFT) | >+ (0x07 << AC_AMPCAP_STEP_SIZE_SHIFT) | >+ (0 << AC_AMPCAP_MUTE_SHIFT)); >+ >+ if (!spec->adc_nids && spec->input_mux) { >+ /* check whether NID 0x07 is valid */ >+ unsigned int wcap = get_wcaps(codec, 0x07); >+ int i; >+ >+ /* get type */ >+ wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; >+ if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) { >+ spec->adc_nids = alc268_adc_nids_alt; >+ spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt); >+ spec->mixers[spec->num_mixers] = >+ alc268_capture_alt_mixer; >+ spec->num_mixers++; >+ } else { >+ spec->adc_nids = alc268_adc_nids; >+ spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids); >+ spec->mixers[spec->num_mixers] = >+ alc268_capture_mixer; >+ spec->num_mixers++; >+ } >+ spec->capsrc_nids = alc268_capsrc_nids; >+ /* set default input source */ >+ for (i = 0; i < spec->num_adc_nids; i++) >+ snd_hda_codec_write_cache(codec, alc268_capsrc_nids[i], >+ 0, AC_VERB_SET_CONNECT_SEL, >+ spec->input_mux->items[0].index); >+ } >+ >+ spec->vmaster_nid = 0x02; >+ >+ codec->patch_ops = alc_patch_ops; >+ if (board_config == ALC268_AUTO) >+ spec->init_hook = alc268_auto_init; >+ >+ return 0; >+} >+ >+/* >+ * ALC269 channel source setting (2 channel) >+ */ >+#define ALC269_DIGOUT_NID ALC880_DIGOUT_NID >+ >+#define alc269_dac_nids alc260_dac_nids >+ >+static hda_nid_t alc269_adc_nids[1] = { >+ /* ADC1 */ >+ 0x07, >+}; >+ >+#define alc269_modes alc260_modes >+#define alc269_capture_source alc880_lg_lw_capture_source >+ >+static struct snd_kcontrol_new alc269_base_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), >+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT), >+ { } /* end */ >+}; >+ >+/* capture mixer elements */ >+static struct snd_kcontrol_new alc269_capture_mixer[] = { >+ HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ /* The multiple "Capture Source" controls confuse alsamixer >+ * So call somewhat different.. >+ */ >+ /* .name = "Capture Source", */ >+ .name = "Input Source", >+ .count = 1, >+ .info = alc_mux_enum_info, >+ .get = alc_mux_enum_get, >+ .put = alc_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+/* >+ * generic initialization of ADC, input mixers and output mixers >+ */ >+static struct hda_verb alc269_init_verbs[] = { >+ /* >+ * Unmute ADC0 and set the default input to mic-in >+ */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ >+ /* Mute input amps (PCBeep, Line In, Mic 1 & Mic 2) of the >+ * analog-loopback mixer widget >+ * Note: PASD motherboards uses the Line In 2 as the input for >+ * front panel mic (mic 2) >+ */ >+ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, >+ >+ /* >+ * Set up output mixers (0x0c - 0x0e) >+ */ >+ /* set vol=0 to output mixers */ >+ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ >+ /* set up input amps for analog loopback */ >+ /* Amp Indices: DAC = 0, mixer = 1 */ >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ >+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ >+ /* FIXME: use matrix-type input source selection */ >+ /* Mixer elements: 0x18, 19, 1a, 1b, 1d, 0b */ >+ /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, >+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, >+ >+ /* set EAPD */ >+ {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, >+ {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, >+ { } >+}; >+ >+/* add playback controls from the parsed DAC table */ >+static int alc269_auto_create_multi_out_ctls(struct alc_spec *spec, >+ const struct auto_pin_cfg *cfg) >+{ >+ hda_nid_t nid; >+ int err; >+ >+ spec->multiout.num_dacs = 1; /* only use one dac */ >+ spec->multiout.dac_nids = spec->private_dac_nids; >+ spec->multiout.dac_nids[0] = 2; >+ >+ nid = cfg->line_out_pins[0]; >+ if (nid) { >+ err = add_control(spec, ALC_CTL_WIDGET_VOL, >+ "Front Playback Volume", >+ HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ err = add_control(spec, ALC_CTL_WIDGET_MUTE, >+ "Front Playback Switch", >+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ } >+ >+ nid = cfg->speaker_pins[0]; >+ if (nid) { >+ if (!cfg->line_out_pins[0]) { >+ err = add_control(spec, ALC_CTL_WIDGET_VOL, >+ "Speaker Playback Volume", >+ HDA_COMPOSE_AMP_VAL(0x02, 3, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ } >+ if (nid == 0x16) { >+ err = add_control(spec, ALC_CTL_WIDGET_MUTE, >+ "Speaker Playback Switch", >+ HDA_COMPOSE_AMP_VAL(nid, 2, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ } else { >+ err = add_control(spec, ALC_CTL_WIDGET_MUTE, >+ "Speaker Playback Switch", >+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ } >+ } >+ nid = cfg->hp_pins[0]; >+ if (nid) { >+ /* spec->multiout.hp_nid = 2; */ >+ if (!cfg->line_out_pins[0] && !cfg->speaker_pins[0]) { >+ err = add_control(spec, ALC_CTL_WIDGET_VOL, >+ "Headphone Playback Volume", >+ HDA_COMPOSE_AMP_VAL(0x02, 3, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ } >+ if (nid == 0x16) { >+ err = add_control(spec, ALC_CTL_WIDGET_MUTE, >+ "Headphone Playback Switch", >+ HDA_COMPOSE_AMP_VAL(nid, 2, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ } else { >+ err = add_control(spec, ALC_CTL_WIDGET_MUTE, >+ "Headphone Playback Switch", >+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ } >+ } >+ return 0; >+} >+ >+#define alc269_auto_create_analog_input_ctls \ >+ alc880_auto_create_analog_input_ctls >+ >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+#define alc269_loopbacks alc880_loopbacks >+#endif >+ >+/* pcm configuration: identiacal with ALC880 */ >+#define alc269_pcm_analog_playback alc880_pcm_analog_playback >+#define alc269_pcm_analog_capture alc880_pcm_analog_capture >+#define alc269_pcm_digital_playback alc880_pcm_digital_playback >+#define alc269_pcm_digital_capture alc880_pcm_digital_capture >+ >+/* >+ * BIOS auto configuration >+ */ >+static int alc269_parse_auto_config(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ int err; >+ static hda_nid_t alc269_ignore[] = { 0x1d, 0 }; >+ >+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, >+ alc269_ignore); >+ if (err < 0) >+ return err; >+ >+ err = alc269_auto_create_multi_out_ctls(spec, &spec->autocfg); >+ if (err < 0) >+ return err; >+ err = alc269_auto_create_analog_input_ctls(spec, &spec->autocfg); >+ if (err < 0) >+ return err; >+ >+ spec->multiout.max_channels = spec->multiout.num_dacs * 2; >+ >+ if (spec->autocfg.dig_out_pin) >+ spec->multiout.dig_out_nid = ALC269_DIGOUT_NID; >+ >+ if (spec->kctl_alloc) >+ spec->mixers[spec->num_mixers++] = spec->kctl_alloc; >+ >+ spec->init_verbs[spec->num_init_verbs++] = alc269_init_verbs; >+ spec->num_mux_defs = 1; >+ spec->input_mux = &spec->private_imux; >+ >+ err = alc_auto_add_mic_boost(codec); >+ if (err < 0) >+ return err; >+ >+ return 1; >+} >+ >+#define alc269_auto_init_multi_out alc882_auto_init_multi_out >+#define alc269_auto_init_hp_out alc882_auto_init_hp_out >+#define alc269_auto_init_analog_input alc882_auto_init_analog_input >+ >+ >+/* init callback for auto-configuration model -- overriding the default init */ >+static void alc269_auto_init(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ alc269_auto_init_multi_out(codec); >+ alc269_auto_init_hp_out(codec); >+ alc269_auto_init_analog_input(codec); >+ if (spec->unsol_event) >+ alc_sku_automute(codec); >+} >+ >+/* >+ * configuration and preset >+ */ >+static const char *alc269_models[ALC269_MODEL_LAST] = { >+ [ALC269_BASIC] = "basic", >+}; >+ >+static struct snd_pci_quirk alc269_cfg_tbl[] = { >+ {} >+}; >+ >+static struct alc_config_preset alc269_presets[] = { >+ [ALC269_BASIC] = { >+ .mixers = { alc269_base_mixer }, >+ .init_verbs = { alc269_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc269_dac_nids), >+ .dac_nids = alc269_dac_nids, >+ .hp_nid = 0x03, >+ .num_channel_mode = ARRAY_SIZE(alc269_modes), >+ .channel_mode = alc269_modes, >+ .input_mux = &alc269_capture_source, >+ }, >+}; >+ >+static int patch_alc269(struct hda_codec *codec) >+{ >+ struct alc_spec *spec; >+ int board_config; >+ int err; >+ >+ spec = kzalloc(sizeof(*spec), GFP_KERNEL); >+ if (spec == NULL) >+ return -ENOMEM; >+ >+ codec->spec = spec; >+ >+ alc_fix_pll_init(codec, 0x20, 0x04, 15); >+ >+ board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST, >+ alc269_models, >+ alc269_cfg_tbl); >+ >+ if (board_config < 0) { >+ printk(KERN_INFO "hda_codec: Unknown model for ALC269, " >+ "trying auto-probe from BIOS...\n"); >+ board_config = ALC269_AUTO; >+ } >+ >+ if (board_config == ALC269_AUTO) { >+ /* automatic parse from the BIOS config */ >+ err = alc269_parse_auto_config(codec); >+ if (err < 0) { >+ alc_free(codec); >+ return err; >+ } else if (!err) { >+ printk(KERN_INFO >+ "hda_codec: Cannot set up configuration " >+ "from BIOS. Using base mode...\n"); >+ board_config = ALC269_BASIC; >+ } >+ } >+ >+ if (board_config != ALC269_AUTO) >+ setup_preset(spec, &alc269_presets[board_config]); >+ >+ spec->stream_name_analog = "ALC269 Analog"; >+ spec->stream_analog_playback = &alc269_pcm_analog_playback; >+ spec->stream_analog_capture = &alc269_pcm_analog_capture; >+ >+ spec->stream_name_digital = "ALC269 Digital"; >+ spec->stream_digital_playback = &alc269_pcm_digital_playback; >+ spec->stream_digital_capture = &alc269_pcm_digital_capture; >+ >+ spec->adc_nids = alc269_adc_nids; >+ spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids); >+ spec->mixers[spec->num_mixers] = alc269_capture_mixer; >+ spec->num_mixers++; >+ >+ codec->patch_ops = alc_patch_ops; >+ if (board_config == ALC269_AUTO) >+ spec->init_hook = alc269_auto_init; >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+ if (!spec->loopback.amplist) >+ spec->loopback.amplist = alc269_loopbacks; >+#endif >+ >+ return 0; >+} >+ >+/* >+ * ALC861 channel source setting (2/6 channel selection for 3-stack) >+ */ >+ >+/* >+ * set the path ways for 2 channel output >+ * need to set the codec line out and mic 1 pin widgets to inputs >+ */ >+static struct hda_verb alc861_threestack_ch2_init[] = { >+ /* set pin widget 1Ah (line in) for input */ >+ { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, >+ /* set pin widget 18h (mic1/2) for input, for mic also enable >+ * the vref >+ */ >+ { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, >+ >+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c }, >+#if 0 >+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/ >+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, /*line-in*/ >+#endif >+ { } /* end */ >+}; >+/* >+ * 6ch mode >+ * need to set the codec line out and mic 1 pin widgets to outputs >+ */ >+static struct hda_verb alc861_threestack_ch6_init[] = { >+ /* set pin widget 1Ah (line in) for output (Back Surround)*/ >+ { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, >+ /* set pin widget 18h (mic1) for output (CLFE)*/ >+ { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, >+ >+ { 0x0c, AC_VERB_SET_CONNECT_SEL, 0x00 }, >+ { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00 }, >+ >+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 }, >+#if 0 >+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/ >+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8)) }, /*line in*/ >+#endif >+ { } /* end */ >+}; >+ >+static struct hda_channel_mode alc861_threestack_modes[2] = { >+ { 2, alc861_threestack_ch2_init }, >+ { 6, alc861_threestack_ch6_init }, >+}; >+/* Set mic1 as input and unmute the mixer */ >+static struct hda_verb alc861_uniwill_m31_ch2_init[] = { >+ { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, >+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/ >+ { } /* end */ >+}; >+/* Set mic1 as output and mute mixer */ >+static struct hda_verb alc861_uniwill_m31_ch4_init[] = { >+ { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, >+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/ >+ { } /* end */ >+}; >+ >+static struct hda_channel_mode alc861_uniwill_m31_modes[2] = { >+ { 2, alc861_uniwill_m31_ch2_init }, >+ { 4, alc861_uniwill_m31_ch4_init }, >+}; >+ >+/* Set mic1 and line-in as input and unmute the mixer */ >+static struct hda_verb alc861_asus_ch2_init[] = { >+ /* set pin widget 1Ah (line in) for input */ >+ { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, >+ /* set pin widget 18h (mic1/2) for input, for mic also enable >+ * the vref >+ */ >+ { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, >+ >+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c }, >+#if 0 >+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/ >+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, /*line-in*/ >+#endif >+ { } /* end */ >+}; >+/* Set mic1 nad line-in as output and mute mixer */ >+static struct hda_verb alc861_asus_ch6_init[] = { >+ /* set pin widget 1Ah (line in) for output (Back Surround)*/ >+ { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, >+ /* { 0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, */ >+ /* set pin widget 18h (mic1) for output (CLFE)*/ >+ { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, >+ /* { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, */ >+ { 0x0c, AC_VERB_SET_CONNECT_SEL, 0x00 }, >+ { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00 }, >+ >+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 }, >+#if 0 >+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/ >+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8)) }, /*line in*/ >+#endif >+ { } /* end */ >+}; >+ >+static struct hda_channel_mode alc861_asus_modes[2] = { >+ { 2, alc861_asus_ch2_init }, >+ { 6, alc861_asus_ch6_init }, >+}; >+ >+/* patch-ALC861 */ >+ >+static struct snd_kcontrol_new alc861_base_mixer[] = { >+ /* output mixer control */ >+ HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 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", 0x04, 0x0, HDA_OUTPUT), >+ >+ /*Input mixer control */ >+ /* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */ >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT), >+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT), >+ >+ /* Capture mixer control */ >+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Capture Source", >+ .count = 1, >+ .info = alc_mux_enum_info, >+ .get = alc_mux_enum_get, >+ .put = alc_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc861_3ST_mixer[] = { >+ /* output mixer control */ >+ HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 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", 0x04, 0x0, HDA_OUTPUT), */ >+ >+ /* Input mixer control */ >+ /* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */ >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT), >+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT), >+ >+ /* Capture mixer control */ >+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Capture Source", >+ .count = 1, >+ .info = alc_mux_enum_info, >+ .get = alc_mux_enum_get, >+ .put = alc_mux_enum_put, >+ }, >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Channel Mode", >+ .info = alc_ch_mode_info, >+ .get = alc_ch_mode_get, >+ .put = alc_ch_mode_put, >+ .private_value = ARRAY_SIZE(alc861_threestack_modes), >+ }, >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc861_toshiba_mixer[] = { >+ /* output mixer control */ >+ HDA_CODEC_MUTE("Master Playback Switch", 0x03, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT), >+ >+ /*Capture mixer control */ >+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Capture Source", >+ .count = 1, >+ .info = alc_mux_enum_info, >+ .get = alc_mux_enum_get, >+ .put = alc_mux_enum_put, >+ }, >+ >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc861_uniwill_m31_mixer[] = { >+ /* output mixer control */ >+ HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 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", 0x04, 0x0, HDA_OUTPUT), */ >+ >+ /* Input mixer control */ >+ /* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */ >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT), >+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT), >+ >+ /* Capture mixer control */ >+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Capture Source", >+ .count = 1, >+ .info = alc_mux_enum_info, >+ .get = alc_mux_enum_get, >+ .put = alc_mux_enum_put, >+ }, >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Channel Mode", >+ .info = alc_ch_mode_info, >+ .get = alc_ch_mode_get, >+ .put = alc_ch_mode_put, >+ .private_value = ARRAY_SIZE(alc861_uniwill_m31_modes), >+ }, >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc861_asus_mixer[] = { >+ /* output mixer control */ >+ HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 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", 0x04, 0x0, HDA_OUTPUT), >+ >+ /* Input mixer control */ >+ HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT), >+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_OUTPUT), >+ >+ /* Capture mixer control */ >+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Capture Source", >+ .count = 1, >+ .info = alc_mux_enum_info, >+ .get = alc_mux_enum_get, >+ .put = alc_mux_enum_put, >+ }, >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Channel Mode", >+ .info = alc_ch_mode_info, >+ .get = alc_ch_mode_get, >+ .put = alc_ch_mode_put, >+ .private_value = ARRAY_SIZE(alc861_asus_modes), >+ }, >+ { } >+}; >+ >+/* additional mixer */ >+static struct snd_kcontrol_new alc861_asus_laptop_mixer[] = { >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x23, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("PC Beep Playback Switch", 0x23, 0x0, HDA_OUTPUT), >+ { } >+}; >+ >+/* >+ * generic initialization of ADC, input mixers and output mixers >+ */ >+static struct hda_verb alc861_base_init_verbs[] = { >+ /* >+ * Unmute ADC0 and set the default input to mic-in >+ */ >+ /* port-A for surround (rear panel) */ >+ { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, >+ { 0x0e, AC_VERB_SET_CONNECT_SEL, 0x00 }, >+ /* port-B for mic-in (rear panel) with vref */ >+ { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, >+ /* port-C for line-in (rear panel) */ >+ { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, >+ /* port-D for Front */ >+ { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, >+ { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 }, >+ /* port-E for HP out (front panel) */ >+ { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, >+ /* route front PCM to HP */ >+ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, >+ /* port-F for mic-in (front panel) with vref */ >+ { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, >+ /* port-G for CLFE (rear panel) */ >+ { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, >+ { 0x1f, AC_VERB_SET_CONNECT_SEL, 0x00 }, >+ /* port-H for side (rear panel) */ >+ { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, >+ { 0x20, AC_VERB_SET_CONNECT_SEL, 0x00 }, >+ /* CD-in */ >+ { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, >+ /* route front mic to ADC1*/ >+ {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ >+ /* Unmute DAC0~3 & spdif out*/ >+ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ >+ /* Unmute Mixer 14 (mic) 1c (Line in)*/ >+ {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ >+ /* Unmute Stereo Mixer 15 */ >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, /* Output 0~12 step */ >+ >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ /* hp used DAC 3 (Front) */ >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, >+ >+ { } >+}; >+ >+static struct hda_verb alc861_threestack_init_verbs[] = { >+ /* >+ * Unmute ADC0 and set the default input to mic-in >+ */ >+ /* port-A for surround (rear panel) */ >+ { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, >+ /* port-B for mic-in (rear panel) with vref */ >+ { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, >+ /* port-C for line-in (rear panel) */ >+ { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, >+ /* port-D for Front */ >+ { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, >+ { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 }, >+ /* port-E for HP out (front panel) */ >+ { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, >+ /* route front PCM to HP */ >+ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, >+ /* port-F for mic-in (front panel) with vref */ >+ { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, >+ /* port-G for CLFE (rear panel) */ >+ { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, >+ /* port-H for side (rear panel) */ >+ { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, >+ /* CD-in */ >+ { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, >+ /* route front mic to ADC1*/ >+ {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ /* Unmute DAC0~3 & spdif out*/ >+ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ >+ /* Unmute Mixer 14 (mic) 1c (Line in)*/ >+ {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ >+ /* Unmute Stereo Mixer 15 */ >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, /* Output 0~12 step */ >+ >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ /* hp used DAC 3 (Front) */ >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, >+ { } >+}; >+ >+static struct hda_verb alc861_uniwill_m31_init_verbs[] = { >+ /* >+ * Unmute ADC0 and set the default input to mic-in >+ */ >+ /* port-A for surround (rear panel) */ >+ { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, >+ /* port-B for mic-in (rear panel) with vref */ >+ { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, >+ /* port-C for line-in (rear panel) */ >+ { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, >+ /* port-D for Front */ >+ { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, >+ { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 }, >+ /* port-E for HP out (front panel) */ >+ /* this has to be set to VREF80 */ >+ { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, >+ /* route front PCM to HP */ >+ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, >+ /* port-F for mic-in (front panel) with vref */ >+ { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, >+ /* port-G for CLFE (rear panel) */ >+ { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, >+ /* port-H for side (rear panel) */ >+ { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, >+ /* CD-in */ >+ { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, >+ /* route front mic to ADC1*/ >+ {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ /* Unmute DAC0~3 & spdif out*/ >+ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ >+ /* Unmute Mixer 14 (mic) 1c (Line in)*/ >+ {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ >+ /* Unmute Stereo Mixer 15 */ >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, /* Output 0~12 step */ >+ >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ /* hp used DAC 3 (Front) */ >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, >+ { } >+}; >+ >+static struct hda_verb alc861_asus_init_verbs[] = { >+ /* >+ * Unmute ADC0 and set the default input to mic-in >+ */ >+ /* port-A for surround (rear panel) >+ * according to codec#0 this is the HP jack >+ */ >+ { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, /* was 0x00 */ >+ /* route front PCM to HP */ >+ { 0x0e, AC_VERB_SET_CONNECT_SEL, 0x01 }, >+ /* port-B for mic-in (rear panel) with vref */ >+ { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, >+ /* port-C for line-in (rear panel) */ >+ { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, >+ /* port-D for Front */ >+ { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, >+ { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 }, >+ /* port-E for HP out (front panel) */ >+ /* this has to be set to VREF80 */ >+ { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, >+ /* route front PCM to HP */ >+ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, >+ /* port-F for mic-in (front panel) with vref */ >+ { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, >+ /* port-G for CLFE (rear panel) */ >+ { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, >+ /* port-H for side (rear panel) */ >+ { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, >+ /* CD-in */ >+ { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, >+ /* route front mic to ADC1*/ >+ {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ /* Unmute DAC0~3 & spdif out*/ >+ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ /* Unmute Mixer 14 (mic) 1c (Line in)*/ >+ {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ >+ /* Unmute Stereo Mixer 15 */ >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, /* Output 0~12 step */ >+ >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ /* hp used DAC 3 (Front) */ >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, >+ { } >+}; >+ >+/* additional init verbs for ASUS laptops */ >+static struct hda_verb alc861_asus_laptop_init_verbs[] = { >+ { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x45 }, /* HP-out */ >+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2) }, /* mute line-in */ >+ { } >+}; >+ >+/* >+ * generic initialization of ADC, input mixers and output mixers >+ */ >+static struct hda_verb alc861_auto_init_verbs[] = { >+ /* >+ * Unmute ADC0 and set the default input to mic-in >+ */ >+ /* {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, */ >+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ >+ /* Unmute DAC0~3 & spdif out*/ >+ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ >+ /* Unmute Mixer 14 (mic) 1c (Line in)*/ >+ {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ >+ /* Unmute Stereo Mixer 15 */ >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, >+ >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, >+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, >+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, >+ >+ {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, /* set Mic 1 */ >+ >+ { } >+}; >+ >+static struct hda_verb alc861_toshiba_init_verbs[] = { >+ {0x0f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, >+ >+ { } >+}; >+ >+/* toggle speaker-output according to the hp-jack state */ >+static void alc861_toshiba_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ >+ present = snd_hda_codec_read(codec, 0x0f, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ snd_hda_codec_amp_stereo(codec, 0x16, HDA_INPUT, 0, >+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); >+ snd_hda_codec_amp_stereo(codec, 0x1a, HDA_INPUT, 3, >+ HDA_AMP_MUTE, present ? 0 : HDA_AMP_MUTE); >+} >+ >+static void alc861_toshiba_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ if ((res >> 26) == ALC880_HP_EVENT) >+ alc861_toshiba_automute(codec); >+} >+ >+/* pcm configuration: identiacal with ALC880 */ >+#define alc861_pcm_analog_playback alc880_pcm_analog_playback >+#define alc861_pcm_analog_capture alc880_pcm_analog_capture >+#define alc861_pcm_digital_playback alc880_pcm_digital_playback >+#define alc861_pcm_digital_capture alc880_pcm_digital_capture >+ >+ >+#define ALC861_DIGOUT_NID 0x07 >+ >+static struct hda_channel_mode alc861_8ch_modes[1] = { >+ { 8, NULL } >+}; >+ >+static hda_nid_t alc861_dac_nids[4] = { >+ /* front, surround, clfe, side */ >+ 0x03, 0x06, 0x05, 0x04 >+}; >+ >+static hda_nid_t alc660_dac_nids[3] = { >+ /* front, clfe, surround */ >+ 0x03, 0x05, 0x06 >+}; >+ >+static hda_nid_t alc861_adc_nids[1] = { >+ /* ADC0-2 */ >+ 0x08, >+}; >+ >+static struct hda_input_mux alc861_capture_source = { >+ .num_items = 5, >+ .items = { >+ { "Mic", 0x0 }, >+ { "Front Mic", 0x3 }, >+ { "Line", 0x1 }, >+ { "CD", 0x4 }, >+ { "Mixer", 0x5 }, >+ }, >+}; >+ >+/* fill in the dac_nids table from the parsed pin configuration */ >+static int alc861_auto_fill_dac_nids(struct alc_spec *spec, >+ const struct auto_pin_cfg *cfg) >+{ >+ int i; >+ hda_nid_t nid; >+ >+ spec->multiout.dac_nids = spec->private_dac_nids; >+ for (i = 0; i < cfg->line_outs; i++) { >+ nid = cfg->line_out_pins[i]; >+ if (nid) { >+ if (i >= ARRAY_SIZE(alc861_dac_nids)) >+ continue; >+ spec->multiout.dac_nids[i] = alc861_dac_nids[i]; >+ } >+ } >+ spec->multiout.num_dacs = cfg->line_outs; >+ return 0; >+} >+ >+/* add playback controls from the parsed DAC table */ >+static int alc861_auto_create_multi_out_ctls(struct alc_spec *spec, >+ const struct auto_pin_cfg *cfg) >+{ >+ char name[32]; >+ static const char *chname[4] = { >+ "Front", "Surround", NULL /*CLFE*/, "Side" >+ }; >+ hda_nid_t nid; >+ int i, idx, err; >+ >+ for (i = 0; i < cfg->line_outs; i++) { >+ nid = spec->multiout.dac_nids[i]; >+ if (!nid) >+ continue; >+ if (nid == 0x05) { >+ /* Center/LFE */ >+ err = add_control(spec, ALC_CTL_BIND_MUTE, >+ "Center Playback Switch", >+ HDA_COMPOSE_AMP_VAL(nid, 1, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ err = add_control(spec, ALC_CTL_BIND_MUTE, >+ "LFE Playback Switch", >+ HDA_COMPOSE_AMP_VAL(nid, 2, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ } else { >+ for (idx = 0; idx < ARRAY_SIZE(alc861_dac_nids) - 1; >+ idx++) >+ if (nid == alc861_dac_nids[idx]) >+ break; >+ sprintf(name, "%s Playback Switch", chname[idx]); >+ err = add_control(spec, ALC_CTL_BIND_MUTE, name, >+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ } >+ } >+ return 0; >+} >+ >+static int alc861_auto_create_hp_ctls(struct alc_spec *spec, hda_nid_t pin) >+{ >+ int err; >+ hda_nid_t nid; >+ >+ if (!pin) >+ return 0; >+ >+ if ((pin >= 0x0b && pin <= 0x10) || pin == 0x1f || pin == 0x20) { >+ nid = 0x03; >+ err = add_control(spec, ALC_CTL_WIDGET_MUTE, >+ "Headphone Playback Switch", >+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ spec->multiout.hp_nid = nid; >+ } >+ return 0; >+} >+ >+/* create playback/capture controls for input pins */ >+static int alc861_auto_create_analog_input_ctls(struct alc_spec *spec, >+ const struct auto_pin_cfg *cfg) >+{ >+ struct hda_input_mux *imux = &spec->private_imux; >+ int i, err, idx, idx1; >+ >+ for (i = 0; i < AUTO_PIN_LAST; i++) { >+ switch (cfg->input_pins[i]) { >+ case 0x0c: >+ idx1 = 1; >+ idx = 2; /* Line In */ >+ break; >+ case 0x0f: >+ idx1 = 2; >+ idx = 2; /* Line In */ >+ break; >+ case 0x0d: >+ idx1 = 0; >+ idx = 1; /* Mic In */ >+ break; >+ case 0x10: >+ idx1 = 3; >+ idx = 1; /* Mic In */ >+ break; >+ case 0x11: >+ idx1 = 4; >+ idx = 0; /* CD */ >+ break; >+ default: >+ continue; >+ } >+ >+ err = new_analog_input(spec, cfg->input_pins[i], >+ auto_pin_cfg_labels[i], idx, 0x15); >+ if (err < 0) >+ return err; >+ >+ imux->items[imux->num_items].label = auto_pin_cfg_labels[i]; >+ imux->items[imux->num_items].index = idx1; >+ imux->num_items++; >+ } >+ return 0; >+} >+ >+static struct snd_kcontrol_new alc861_capture_mixer[] = { >+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), >+ >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ /* The multiple "Capture Source" controls confuse alsamixer >+ * So call somewhat different.. >+ */ >+ /* .name = "Capture Source", */ >+ .name = "Input Source", >+ .count = 1, >+ .info = alc_mux_enum_info, >+ .get = alc_mux_enum_get, >+ .put = alc_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+static void alc861_auto_set_output_and_unmute(struct hda_codec *codec, >+ hda_nid_t nid, >+ int pin_type, int dac_idx) >+{ >+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, >+ pin_type); >+ snd_hda_codec_write(codec, dac_idx, 0, AC_VERB_SET_AMP_GAIN_MUTE, >+ AMP_OUT_UNMUTE); >+} >+ >+static void alc861_auto_init_multi_out(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ int i; >+ >+ alc_subsystem_id(codec, 0x0e, 0x0f, 0x0b); >+ for (i = 0; i < spec->autocfg.line_outs; i++) { >+ hda_nid_t nid = spec->autocfg.line_out_pins[i]; >+ int pin_type = get_pin_type(spec->autocfg.line_out_type); >+ if (nid) >+ alc861_auto_set_output_and_unmute(codec, nid, pin_type, >+ spec->multiout.dac_nids[i]); >+ } >+} >+ >+static void alc861_auto_init_hp_out(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ hda_nid_t pin; >+ >+ pin = spec->autocfg.hp_pins[0]; >+ if (pin) /* connect to front */ >+ alc861_auto_set_output_and_unmute(codec, pin, PIN_HP, >+ spec->multiout.dac_nids[0]); >+ pin = spec->autocfg.speaker_pins[0]; >+ if (pin) >+ alc861_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); >+} >+ >+static void alc861_auto_init_analog_input(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ int i; >+ >+ for (i = 0; i < AUTO_PIN_LAST; i++) { >+ hda_nid_t nid = spec->autocfg.input_pins[i]; >+ if (nid >= 0x0c && nid <= 0x11) { >+ snd_hda_codec_write(codec, nid, 0, >+ AC_VERB_SET_PIN_WIDGET_CONTROL, >+ i <= AUTO_PIN_FRONT_MIC ? >+ PIN_VREF80 : PIN_IN); >+ } >+ } >+} >+ >+/* 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 alc861_parse_auto_config(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ int err; >+ static hda_nid_t alc861_ignore[] = { 0x1d, 0 }; >+ >+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, >+ alc861_ignore); >+ if (err < 0) >+ return err; >+ if (!spec->autocfg.line_outs) >+ return 0; /* can't find valid BIOS pin config */ >+ >+ err = alc861_auto_fill_dac_nids(spec, &spec->autocfg); >+ if (err < 0) >+ return err; >+ err = alc861_auto_create_multi_out_ctls(spec, &spec->autocfg); >+ if (err < 0) >+ return err; >+ err = alc861_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); >+ if (err < 0) >+ return err; >+ err = alc861_auto_create_analog_input_ctls(spec, &spec->autocfg); >+ if (err < 0) >+ return err; >+ >+ spec->multiout.max_channels = spec->multiout.num_dacs * 2; >+ >+ if (spec->autocfg.dig_out_pin) >+ spec->multiout.dig_out_nid = ALC861_DIGOUT_NID; >+ >+ if (spec->kctl_alloc) >+ spec->mixers[spec->num_mixers++] = spec->kctl_alloc; >+ >+ spec->init_verbs[spec->num_init_verbs++] = alc861_auto_init_verbs; >+ >+ spec->num_mux_defs = 1; >+ spec->input_mux = &spec->private_imux; >+ >+ spec->adc_nids = alc861_adc_nids; >+ spec->num_adc_nids = ARRAY_SIZE(alc861_adc_nids); >+ spec->mixers[spec->num_mixers] = alc861_capture_mixer; >+ spec->num_mixers++; >+ >+ return 1; >+} >+ >+/* additional initialization for auto-configuration model */ >+static void alc861_auto_init(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ alc861_auto_init_multi_out(codec); >+ alc861_auto_init_hp_out(codec); >+ alc861_auto_init_analog_input(codec); >+ if (spec->unsol_event) >+ alc_sku_automute(codec); >+} >+ >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+static struct hda_amp_list alc861_loopbacks[] = { >+ { 0x15, HDA_INPUT, 0 }, >+ { 0x15, HDA_INPUT, 1 }, >+ { 0x15, HDA_INPUT, 2 }, >+ { 0x15, HDA_INPUT, 3 }, >+ { } /* end */ >+}; >+#endif >+ >+ >+/* >+ * configuration and preset >+ */ >+static const char *alc861_models[ALC861_MODEL_LAST] = { >+ [ALC861_3ST] = "3stack", >+ [ALC660_3ST] = "3stack-660", >+ [ALC861_3ST_DIG] = "3stack-dig", >+ [ALC861_6ST_DIG] = "6stack-dig", >+ [ALC861_UNIWILL_M31] = "uniwill-m31", >+ [ALC861_TOSHIBA] = "toshiba", >+ [ALC861_ASUS] = "asus", >+ [ALC861_ASUS_LAPTOP] = "asus-laptop", >+ [ALC861_AUTO] = "auto", >+}; >+ >+static struct snd_pci_quirk alc861_cfg_tbl[] = { >+ SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC861_3ST), >+ SND_PCI_QUIRK(0x1043, 0x1335, "ASUS F2/3", ALC861_ASUS_LAPTOP), >+ SND_PCI_QUIRK(0x1043, 0x1338, "ASUS F2/3", ALC861_ASUS_LAPTOP), >+ SND_PCI_QUIRK(0x1043, 0x1393, "ASUS", ALC861_ASUS), >+ SND_PCI_QUIRK(0x1043, 0x13d7, "ASUS A9rp", ALC861_ASUS_LAPTOP), >+ SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS P1-AH2", ALC861_3ST_DIG), >+ SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba", ALC861_TOSHIBA), >+ /* FIXME: the entry below breaks Toshiba A100 (model=auto works!) >+ * Any other models that need this preset? >+ */ >+ /* SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba", ALC861_TOSHIBA), */ >+ SND_PCI_QUIRK(0x1462, 0x7254, "HP dx2200 (MSI MS-7254)", ALC861_3ST), >+ SND_PCI_QUIRK(0x1462, 0x7297, "HP dx2250 (MSI MS-7297)", ALC861_3ST), >+ SND_PCI_QUIRK(0x1584, 0x2b01, "Uniwill X40AIx", ALC861_UNIWILL_M31), >+ SND_PCI_QUIRK(0x1584, 0x9072, "Uniwill m31", ALC861_UNIWILL_M31), >+ SND_PCI_QUIRK(0x1584, 0x9075, "Airis Praxis N1212", ALC861_ASUS_LAPTOP), >+ /* FIXME: the below seems conflict */ >+ /* SND_PCI_QUIRK(0x1584, 0x9075, "Uniwill", ALC861_UNIWILL_M31), */ >+ SND_PCI_QUIRK(0x1849, 0x0660, "Asrock 939SLI32", ALC660_3ST), >+ SND_PCI_QUIRK(0x8086, 0xd600, "Intel", ALC861_3ST), >+ {} >+}; >+ >+static struct alc_config_preset alc861_presets[] = { >+ [ALC861_3ST] = { >+ .mixers = { alc861_3ST_mixer }, >+ .init_verbs = { alc861_threestack_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc861_dac_nids), >+ .dac_nids = alc861_dac_nids, >+ .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes), >+ .channel_mode = alc861_threestack_modes, >+ .need_dac_fix = 1, >+ .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), >+ .adc_nids = alc861_adc_nids, >+ .input_mux = &alc861_capture_source, >+ }, >+ [ALC861_3ST_DIG] = { >+ .mixers = { alc861_base_mixer }, >+ .init_verbs = { alc861_threestack_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc861_dac_nids), >+ .dac_nids = alc861_dac_nids, >+ .dig_out_nid = ALC861_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes), >+ .channel_mode = alc861_threestack_modes, >+ .need_dac_fix = 1, >+ .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), >+ .adc_nids = alc861_adc_nids, >+ .input_mux = &alc861_capture_source, >+ }, >+ [ALC861_6ST_DIG] = { >+ .mixers = { alc861_base_mixer }, >+ .init_verbs = { alc861_base_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc861_dac_nids), >+ .dac_nids = alc861_dac_nids, >+ .dig_out_nid = ALC861_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc861_8ch_modes), >+ .channel_mode = alc861_8ch_modes, >+ .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), >+ .adc_nids = alc861_adc_nids, >+ .input_mux = &alc861_capture_source, >+ }, >+ [ALC660_3ST] = { >+ .mixers = { alc861_3ST_mixer }, >+ .init_verbs = { alc861_threestack_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc660_dac_nids), >+ .dac_nids = alc660_dac_nids, >+ .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes), >+ .channel_mode = alc861_threestack_modes, >+ .need_dac_fix = 1, >+ .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), >+ .adc_nids = alc861_adc_nids, >+ .input_mux = &alc861_capture_source, >+ }, >+ [ALC861_UNIWILL_M31] = { >+ .mixers = { alc861_uniwill_m31_mixer }, >+ .init_verbs = { alc861_uniwill_m31_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc861_dac_nids), >+ .dac_nids = alc861_dac_nids, >+ .dig_out_nid = ALC861_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc861_uniwill_m31_modes), >+ .channel_mode = alc861_uniwill_m31_modes, >+ .need_dac_fix = 1, >+ .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), >+ .adc_nids = alc861_adc_nids, >+ .input_mux = &alc861_capture_source, >+ }, >+ [ALC861_TOSHIBA] = { >+ .mixers = { alc861_toshiba_mixer }, >+ .init_verbs = { alc861_base_init_verbs, >+ alc861_toshiba_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc861_dac_nids), >+ .dac_nids = alc861_dac_nids, >+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), >+ .channel_mode = alc883_3ST_2ch_modes, >+ .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), >+ .adc_nids = alc861_adc_nids, >+ .input_mux = &alc861_capture_source, >+ .unsol_event = alc861_toshiba_unsol_event, >+ .init_hook = alc861_toshiba_automute, >+ }, >+ [ALC861_ASUS] = { >+ .mixers = { alc861_asus_mixer }, >+ .init_verbs = { alc861_asus_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc861_dac_nids), >+ .dac_nids = alc861_dac_nids, >+ .dig_out_nid = ALC861_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc861_asus_modes), >+ .channel_mode = alc861_asus_modes, >+ .need_dac_fix = 1, >+ .hp_nid = 0x06, >+ .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), >+ .adc_nids = alc861_adc_nids, >+ .input_mux = &alc861_capture_source, >+ }, >+ [ALC861_ASUS_LAPTOP] = { >+ .mixers = { alc861_toshiba_mixer, alc861_asus_laptop_mixer }, >+ .init_verbs = { alc861_asus_init_verbs, >+ alc861_asus_laptop_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc861_dac_nids), >+ .dac_nids = alc861_dac_nids, >+ .dig_out_nid = ALC861_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), >+ .channel_mode = alc883_3ST_2ch_modes, >+ .need_dac_fix = 1, >+ .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), >+ .adc_nids = alc861_adc_nids, >+ .input_mux = &alc861_capture_source, >+ }, >+}; >+ >+ >+static int patch_alc861(struct hda_codec *codec) >+{ >+ struct alc_spec *spec; >+ int board_config; >+ int err; >+ >+ spec = kzalloc(sizeof(*spec), GFP_KERNEL); >+ if (spec == NULL) >+ return -ENOMEM; >+ >+ codec->spec = spec; >+ >+ board_config = snd_hda_check_board_config(codec, ALC861_MODEL_LAST, >+ alc861_models, >+ alc861_cfg_tbl); >+ >+ if (board_config < 0) { >+ printk(KERN_INFO "hda_codec: Unknown model for ALC861, " >+ "trying auto-probe from BIOS...\n"); >+ board_config = ALC861_AUTO; >+ } >+ >+ if (board_config == ALC861_AUTO) { >+ /* automatic parse from the BIOS config */ >+ err = alc861_parse_auto_config(codec); >+ if (err < 0) { >+ alc_free(codec); >+ return err; >+ } else if (!err) { >+ printk(KERN_INFO >+ "hda_codec: Cannot set up configuration " >+ "from BIOS. Using base mode...\n"); >+ board_config = ALC861_3ST_DIG; >+ } >+ } >+ >+ if (board_config != ALC861_AUTO) >+ setup_preset(spec, &alc861_presets[board_config]); >+ >+ spec->stream_name_analog = "ALC861 Analog"; >+ spec->stream_analog_playback = &alc861_pcm_analog_playback; >+ spec->stream_analog_capture = &alc861_pcm_analog_capture; >+ >+ spec->stream_name_digital = "ALC861 Digital"; >+ spec->stream_digital_playback = &alc861_pcm_digital_playback; >+ spec->stream_digital_capture = &alc861_pcm_digital_capture; >+ >+ spec->vmaster_nid = 0x03; >+ >+ codec->patch_ops = alc_patch_ops; >+ if (board_config == ALC861_AUTO) >+ spec->init_hook = alc861_auto_init; >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+ if (!spec->loopback.amplist) >+ spec->loopback.amplist = alc861_loopbacks; >+#endif >+ >+ return 0; >+} >+ >+/* >+ * ALC861-VD support >+ * >+ * Based on ALC882 >+ * >+ * In addition, an independent DAC >+ */ >+#define ALC861VD_DIGOUT_NID 0x06 >+ >+static hda_nid_t alc861vd_dac_nids[4] = { >+ /* front, surr, clfe, side surr */ >+ 0x02, 0x03, 0x04, 0x05 >+}; >+ >+/* dac_nids for ALC660vd are in a different order - according to >+ * Realtek's driver. >+ * This should probably tesult in a different mixer for 6stack models >+ * of ALC660vd codecs, but for now there is only 3stack mixer >+ * - and it is the same as in 861vd. >+ * adc_nids in ALC660vd are (is) the same as in 861vd >+ */ >+static hda_nid_t alc660vd_dac_nids[3] = { >+ /* front, rear, clfe, rear_surr */ >+ 0x02, 0x04, 0x03 >+}; >+ >+static hda_nid_t alc861vd_adc_nids[1] = { >+ /* ADC0 */ >+ 0x09, >+}; >+ >+static hda_nid_t alc861vd_capsrc_nids[1] = { 0x22 }; >+ >+/* input MUX */ >+/* FIXME: should be a matrix-type input source selection */ >+static struct hda_input_mux alc861vd_capture_source = { >+ .num_items = 4, >+ .items = { >+ { "Mic", 0x0 }, >+ { "Front Mic", 0x1 }, >+ { "Line", 0x2 }, >+ { "CD", 0x4 }, >+ }, >+}; >+ >+static struct hda_input_mux alc861vd_dallas_capture_source = { >+ .num_items = 2, >+ .items = { >+ { "Ext Mic", 0x0 }, >+ { "Int Mic", 0x1 }, >+ }, >+}; >+ >+static struct hda_input_mux alc861vd_hp_capture_source = { >+ .num_items = 2, >+ .items = { >+ { "Front Mic", 0x0 }, >+ { "ATAPI Mic", 0x1 }, >+ }, >+}; >+ >+#define alc861vd_mux_enum_info alc_mux_enum_info >+#define alc861vd_mux_enum_get alc_mux_enum_get >+/* ALC861VD has the ALC882-type input selection (but has only one ADC) */ >+#define alc861vd_mux_enum_put alc882_mux_enum_put >+ >+/* >+ * 2ch mode >+ */ >+static struct hda_channel_mode alc861vd_3stack_2ch_modes[1] = { >+ { 2, NULL } >+}; >+ >+/* >+ * 6ch mode >+ */ >+static struct hda_verb alc861vd_6stack_ch6_init[] = { >+ { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, >+ { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { } /* end */ >+}; >+ >+/* >+ * 8ch mode >+ */ >+static struct hda_verb alc861vd_6stack_ch8_init[] = { >+ { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { } /* end */ >+}; >+ >+static struct hda_channel_mode alc861vd_6stack_modes[2] = { >+ { 6, alc861vd_6stack_ch6_init }, >+ { 8, alc861vd_6stack_ch8_init }, >+}; >+ >+static struct snd_kcontrol_new alc861vd_chmode_mixer[] = { >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Channel Mode", >+ .info = alc_ch_mode_info, >+ .get = alc_ch_mode_get, >+ .put = alc_ch_mode_put, >+ }, >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc861vd_capture_mixer[] = { >+ HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), >+ >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ /* The multiple "Capture Source" controls confuse alsamixer >+ * So call somewhat different.. >+ */ >+ /* .name = "Capture Source", */ >+ .name = "Input Source", >+ .count = 1, >+ .info = alc861vd_mux_enum_info, >+ .get = alc861vd_mux_enum_get, >+ .put = alc861vd_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17 >+ * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b >+ */ >+static struct snd_kcontrol_new alc861vd_6st_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), >+ >+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), >+ >+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, >+ HDA_OUTPUT), >+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, >+ HDA_OUTPUT), >+ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), >+ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), >+ >+ HDA_CODEC_VOLUME("Side Playback Volume", 0x05, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), >+ >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), >+ >+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ >+ HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), >+ >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), >+ >+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), >+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), >+ >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc861vd_3st_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), >+ >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), >+ >+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ >+ HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), >+ >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), >+ >+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), >+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), >+ >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc861vd_lenovo_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), >+ /*HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),*/ >+ HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), >+ >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), >+ >+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ >+ HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), >+ >+ { } /* end */ >+}; >+ >+/* Pin assignment: Speaker=0x14, HP = 0x15, >+ * Ext Mic=0x18, Int Mic = 0x19, CD = 0x1c, PC Beep = 0x1d >+ */ >+static struct snd_kcontrol_new alc861vd_dallas_mixer[] = { >+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Ext Mic Boost", 0x18, 0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Ext Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Ext Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("PC Beep Volume", 0x0b, 0x05, HDA_INPUT), >+ HDA_CODEC_MUTE("PC Beep Switch", 0x0b, 0x05, HDA_INPUT), >+ { } /* end */ >+}; >+ >+/* Pin assignment: Speaker=0x14, Line-out = 0x15, >+ * Front Mic=0x18, ATAPI Mic = 0x19, >+ */ >+static struct snd_kcontrol_new alc861vd_hp_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ >+ { } /* end */ >+}; >+ >+/* >+ * generic initialization of ADC, input mixers and output mixers >+ */ >+static struct hda_verb alc861vd_volume_init_verbs[] = { >+ /* >+ * Unmute ADC0 and set the default input to mic-in >+ */ >+ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ >+ /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of >+ * the analog-loopback mixer widget >+ */ >+ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, >+ >+ /* Capture mixer: unmute Mic, F-Mic, Line, CD inputs */ >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, >+ >+ /* >+ * Set up output mixers (0x02 - 0x05) >+ */ >+ /* set vol=0 to output mixers */ >+ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ >+ /* set up input amps for analog loopback */ >+ /* Amp Indices: DAC = 0, mixer = 1 */ >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ >+ { } >+}; >+ >+/* >+ * 3-stack pin configuration: >+ * front = 0x14, mic/clfe = 0x18, HP = 0x19, line/surr = 0x1a, f-mic = 0x1b >+ */ >+static struct hda_verb alc861vd_3stack_init_verbs[] = { >+ /* >+ * Set pin mode and muting >+ */ >+ /* set front pin widgets 0x14 for output */ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ >+ /* Mic (rear) pin: input vref at 80% */ >+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* Front Mic pin: input vref at 80% */ >+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* Line In pin: input */ >+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* Line-2 In: Headphone output (output 0 - 0x0c) */ >+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ /* CD pin widget for input */ >+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ >+ { } >+}; >+ >+/* >+ * 6-stack pin configuration: >+ */ >+static struct hda_verb alc861vd_6stack_init_verbs[] = { >+ /* >+ * Set pin mode and muting >+ */ >+ /* set front pin widgets 0x14 for output */ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ >+ /* Rear Pin: output 1 (0x0d) */ >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ /* CLFE Pin: output 2 (0x0e) */ >+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x16, AC_VERB_SET_CONNECT_SEL, 0x02}, >+ /* Side Pin: output 3 (0x0f) */ >+ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, >+ >+ /* Mic (rear) pin: input vref at 80% */ >+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* Front Mic pin: input vref at 80% */ >+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* Line In pin: input */ >+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* Line-2 In: Headphone output (output 0 - 0x0c) */ >+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ /* CD pin widget for input */ >+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ >+ { } >+}; >+ >+static struct hda_verb alc861vd_eapd_verbs[] = { >+ {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, >+ { } >+}; >+ >+static struct hda_verb alc660vd_eapd_verbs[] = { >+ {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, >+ {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, >+ { } >+}; >+ >+static struct hda_verb alc861vd_lenovo_unsol_verbs[] = { >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, >+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, >+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, >+ {} >+}; >+ >+/* toggle speaker-output according to the hp-jack state */ >+static void alc861vd_lenovo_hp_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ unsigned char bits; >+ >+ present = snd_hda_codec_read(codec, 0x1b, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ bits = present ? HDA_AMP_MUTE : 0; >+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, bits); >+} >+ >+static void alc861vd_lenovo_mic_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ unsigned char bits; >+ >+ present = snd_hda_codec_read(codec, 0x18, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ bits = present ? HDA_AMP_MUTE : 0; >+ snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, >+ HDA_AMP_MUTE, bits); >+} >+ >+static void alc861vd_lenovo_automute(struct hda_codec *codec) >+{ >+ alc861vd_lenovo_hp_automute(codec); >+ alc861vd_lenovo_mic_automute(codec); >+} >+ >+static void alc861vd_lenovo_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ switch (res >> 26) { >+ case ALC880_HP_EVENT: >+ alc861vd_lenovo_hp_automute(codec); >+ break; >+ case ALC880_MIC_EVENT: >+ alc861vd_lenovo_mic_automute(codec); >+ break; >+ } >+} >+ >+static struct hda_verb alc861vd_dallas_verbs[] = { >+ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ >+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, >+ >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, >+ >+ { } /* end */ >+}; >+ >+/* toggle speaker-output according to the hp-jack state */ >+static void alc861vd_dallas_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ >+ present = snd_hda_codec_read(codec, 0x15, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); >+} >+ >+static void alc861vd_dallas_unsol_event(struct hda_codec *codec, unsigned int res) >+{ >+ if ((res >> 26) == ALC880_HP_EVENT) >+ alc861vd_dallas_automute(codec); >+} >+ >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+#define alc861vd_loopbacks alc880_loopbacks >+#endif >+ >+/* pcm configuration: identiacal with ALC880 */ >+#define alc861vd_pcm_analog_playback alc880_pcm_analog_playback >+#define alc861vd_pcm_analog_capture alc880_pcm_analog_capture >+#define alc861vd_pcm_digital_playback alc880_pcm_digital_playback >+#define alc861vd_pcm_digital_capture alc880_pcm_digital_capture >+ >+/* >+ * configuration and preset >+ */ >+static const char *alc861vd_models[ALC861VD_MODEL_LAST] = { >+ [ALC660VD_3ST] = "3stack-660", >+ [ALC660VD_3ST_DIG] = "3stack-660-digout", >+ [ALC861VD_3ST] = "3stack", >+ [ALC861VD_3ST_DIG] = "3stack-digout", >+ [ALC861VD_6ST_DIG] = "6stack-digout", >+ [ALC861VD_LENOVO] = "lenovo", >+ [ALC861VD_DALLAS] = "dallas", >+ [ALC861VD_HP] = "hp", >+ [ALC861VD_AUTO] = "auto", >+}; >+ >+static struct snd_pci_quirk alc861vd_cfg_tbl[] = { >+ SND_PCI_QUIRK(0x1019, 0xa88d, "Realtek ALC660 demo", ALC660VD_3ST), >+ SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_HP), >+ SND_PCI_QUIRK(0x1043, 0x12e2, "Asus z35m", ALC660VD_3ST), >+ SND_PCI_QUIRK(0x1043, 0x1339, "Asus G1", ALC660VD_3ST), >+ SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660VD_3ST_DIG), >+ SND_PCI_QUIRK(0x10de, 0x03f0, "Realtek ALC660 demo", ALC660VD_3ST), >+ SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO), >+ /*SND_PCI_QUIRK(0x1179, 0xff00, "DALLAS", ALC861VD_DALLAS),*/ /*lenovo*/ >+ SND_PCI_QUIRK(0x1179, 0xff01, "DALLAS", ALC861VD_DALLAS), >+ SND_PCI_QUIRK(0x1179, 0xff03, "Toshiba P205", ALC861VD_LENOVO), >+ SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba L30-149", ALC861VD_DALLAS), >+ SND_PCI_QUIRK(0x1565, 0x820d, "Biostar NF61S SE", ALC861VD_6ST_DIG), >+ SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo", ALC861VD_LENOVO), >+ SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo 3000 C200", ALC861VD_LENOVO), >+ SND_PCI_QUIRK(0x1849, 0x0862, "ASRock K8NF6G-VSTA", ALC861VD_6ST_DIG), >+ {} >+}; >+ >+static struct alc_config_preset alc861vd_presets[] = { >+ [ALC660VD_3ST] = { >+ .mixers = { alc861vd_3st_mixer }, >+ .init_verbs = { alc861vd_volume_init_verbs, >+ alc861vd_3stack_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), >+ .dac_nids = alc660vd_dac_nids, >+ .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), >+ .channel_mode = alc861vd_3stack_2ch_modes, >+ .input_mux = &alc861vd_capture_source, >+ }, >+ [ALC660VD_3ST_DIG] = { >+ .mixers = { alc861vd_3st_mixer }, >+ .init_verbs = { alc861vd_volume_init_verbs, >+ alc861vd_3stack_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), >+ .dac_nids = alc660vd_dac_nids, >+ .dig_out_nid = ALC861VD_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), >+ .channel_mode = alc861vd_3stack_2ch_modes, >+ .input_mux = &alc861vd_capture_source, >+ }, >+ [ALC861VD_3ST] = { >+ .mixers = { alc861vd_3st_mixer }, >+ .init_verbs = { alc861vd_volume_init_verbs, >+ alc861vd_3stack_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), >+ .dac_nids = alc861vd_dac_nids, >+ .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), >+ .channel_mode = alc861vd_3stack_2ch_modes, >+ .input_mux = &alc861vd_capture_source, >+ }, >+ [ALC861VD_3ST_DIG] = { >+ .mixers = { alc861vd_3st_mixer }, >+ .init_verbs = { alc861vd_volume_init_verbs, >+ alc861vd_3stack_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), >+ .dac_nids = alc861vd_dac_nids, >+ .dig_out_nid = ALC861VD_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), >+ .channel_mode = alc861vd_3stack_2ch_modes, >+ .input_mux = &alc861vd_capture_source, >+ }, >+ [ALC861VD_6ST_DIG] = { >+ .mixers = { alc861vd_6st_mixer, alc861vd_chmode_mixer }, >+ .init_verbs = { alc861vd_volume_init_verbs, >+ alc861vd_6stack_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), >+ .dac_nids = alc861vd_dac_nids, >+ .dig_out_nid = ALC861VD_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc861vd_6stack_modes), >+ .channel_mode = alc861vd_6stack_modes, >+ .input_mux = &alc861vd_capture_source, >+ }, >+ [ALC861VD_LENOVO] = { >+ .mixers = { alc861vd_lenovo_mixer }, >+ .init_verbs = { alc861vd_volume_init_verbs, >+ alc861vd_3stack_init_verbs, >+ alc861vd_eapd_verbs, >+ alc861vd_lenovo_unsol_verbs }, >+ .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), >+ .dac_nids = alc660vd_dac_nids, >+ .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), >+ .channel_mode = alc861vd_3stack_2ch_modes, >+ .input_mux = &alc861vd_capture_source, >+ .unsol_event = alc861vd_lenovo_unsol_event, >+ .init_hook = alc861vd_lenovo_automute, >+ }, >+ [ALC861VD_DALLAS] = { >+ .mixers = { alc861vd_dallas_mixer }, >+ .init_verbs = { alc861vd_dallas_verbs }, >+ .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), >+ .dac_nids = alc861vd_dac_nids, >+ .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), >+ .channel_mode = alc861vd_3stack_2ch_modes, >+ .input_mux = &alc861vd_dallas_capture_source, >+ .unsol_event = alc861vd_dallas_unsol_event, >+ .init_hook = alc861vd_dallas_automute, >+ }, >+ [ALC861VD_HP] = { >+ .mixers = { alc861vd_hp_mixer }, >+ .init_verbs = { alc861vd_dallas_verbs, alc861vd_eapd_verbs }, >+ .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), >+ .dac_nids = alc861vd_dac_nids, >+ .dig_out_nid = ALC861VD_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), >+ .channel_mode = alc861vd_3stack_2ch_modes, >+ .input_mux = &alc861vd_hp_capture_source, >+ .unsol_event = alc861vd_dallas_unsol_event, >+ .init_hook = alc861vd_dallas_automute, >+ }, >+}; >+ >+/* >+ * BIOS auto configuration >+ */ >+static void alc861vd_auto_set_output_and_unmute(struct hda_codec *codec, >+ hda_nid_t nid, int pin_type, int dac_idx) >+{ >+ alc_set_pin_output(codec, nid, pin_type); >+} >+ >+static void alc861vd_auto_init_multi_out(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ int i; >+ >+ alc_subsystem_id(codec, 0x15, 0x1b, 0x14); >+ for (i = 0; i <= HDA_SIDE; i++) { >+ hda_nid_t nid = spec->autocfg.line_out_pins[i]; >+ int pin_type = get_pin_type(spec->autocfg.line_out_type); >+ if (nid) >+ alc861vd_auto_set_output_and_unmute(codec, nid, >+ pin_type, i); >+ } >+} >+ >+ >+static void alc861vd_auto_init_hp_out(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ hda_nid_t pin; >+ >+ pin = spec->autocfg.hp_pins[0]; >+ if (pin) /* connect to front and use dac 0 */ >+ alc861vd_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); >+ pin = spec->autocfg.speaker_pins[0]; >+ if (pin) >+ alc861vd_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); >+} >+ >+#define alc861vd_is_input_pin(nid) alc880_is_input_pin(nid) >+#define ALC861VD_PIN_CD_NID ALC880_PIN_CD_NID >+ >+static void alc861vd_auto_init_analog_input(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ int i; >+ >+ for (i = 0; i < AUTO_PIN_LAST; i++) { >+ hda_nid_t nid = spec->autocfg.input_pins[i]; >+ if (alc861vd_is_input_pin(nid)) { >+ snd_hda_codec_write(codec, nid, 0, >+ AC_VERB_SET_PIN_WIDGET_CONTROL, >+ i <= AUTO_PIN_FRONT_MIC ? >+ PIN_VREF80 : PIN_IN); >+ if (nid != ALC861VD_PIN_CD_NID) >+ snd_hda_codec_write(codec, nid, 0, >+ AC_VERB_SET_AMP_GAIN_MUTE, >+ AMP_OUT_MUTE); >+ } >+ } >+} >+ >+#define alc861vd_idx_to_mixer_vol(nid) ((nid) + 0x02) >+#define alc861vd_idx_to_mixer_switch(nid) ((nid) + 0x0c) >+ >+/* add playback controls from the parsed DAC table */ >+/* Based on ALC880 version. But ALC861VD has separate, >+ * different NIDs for mute/unmute switch and volume control */ >+static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec, >+ const struct auto_pin_cfg *cfg) >+{ >+ char name[32]; >+ static const char *chname[4] = {"Front", "Surround", "CLFE", "Side"}; >+ hda_nid_t nid_v, nid_s; >+ int i, err; >+ >+ for (i = 0; i < cfg->line_outs; i++) { >+ if (!spec->multiout.dac_nids[i]) >+ continue; >+ nid_v = alc861vd_idx_to_mixer_vol( >+ alc880_dac_to_idx( >+ spec->multiout.dac_nids[i])); >+ nid_s = alc861vd_idx_to_mixer_switch( >+ alc880_dac_to_idx( >+ spec->multiout.dac_nids[i])); >+ >+ if (i == 2) { >+ /* Center/LFE */ >+ err = add_control(spec, ALC_CTL_WIDGET_VOL, >+ "Center Playback Volume", >+ HDA_COMPOSE_AMP_VAL(nid_v, 1, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ err = add_control(spec, ALC_CTL_WIDGET_VOL, >+ "LFE Playback Volume", >+ HDA_COMPOSE_AMP_VAL(nid_v, 2, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ err = add_control(spec, ALC_CTL_BIND_MUTE, >+ "Center Playback Switch", >+ HDA_COMPOSE_AMP_VAL(nid_s, 1, 2, >+ HDA_INPUT)); >+ if (err < 0) >+ return err; >+ err = add_control(spec, ALC_CTL_BIND_MUTE, >+ "LFE Playback Switch", >+ HDA_COMPOSE_AMP_VAL(nid_s, 2, 2, >+ HDA_INPUT)); >+ if (err < 0) >+ return err; >+ } else { >+ sprintf(name, "%s Playback Volume", chname[i]); >+ err = add_control(spec, ALC_CTL_WIDGET_VOL, name, >+ HDA_COMPOSE_AMP_VAL(nid_v, 3, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ sprintf(name, "%s Playback Switch", chname[i]); >+ err = add_control(spec, ALC_CTL_BIND_MUTE, name, >+ HDA_COMPOSE_AMP_VAL(nid_s, 3, 2, >+ HDA_INPUT)); >+ if (err < 0) >+ return err; >+ } >+ } >+ return 0; >+} >+ >+/* add playback controls for speaker and HP outputs */ >+/* Based on ALC880 version. But ALC861VD has separate, >+ * different NIDs for mute/unmute switch and volume control */ >+static int alc861vd_auto_create_extra_out(struct alc_spec *spec, >+ hda_nid_t pin, const char *pfx) >+{ >+ hda_nid_t nid_v, nid_s; >+ int err; >+ char name[32]; >+ >+ if (!pin) >+ return 0; >+ >+ if (alc880_is_fixed_pin(pin)) { >+ nid_v = alc880_idx_to_dac(alc880_fixed_pin_idx(pin)); >+ /* specify the DAC as the extra output */ >+ if (!spec->multiout.hp_nid) >+ spec->multiout.hp_nid = nid_v; >+ else >+ spec->multiout.extra_out_nid[0] = nid_v; >+ /* control HP volume/switch on the output mixer amp */ >+ nid_v = alc861vd_idx_to_mixer_vol( >+ alc880_fixed_pin_idx(pin)); >+ nid_s = alc861vd_idx_to_mixer_switch( >+ alc880_fixed_pin_idx(pin)); >+ >+ sprintf(name, "%s Playback Volume", pfx); >+ err = add_control(spec, ALC_CTL_WIDGET_VOL, name, >+ HDA_COMPOSE_AMP_VAL(nid_v, 3, 0, HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ sprintf(name, "%s Playback Switch", pfx); >+ err = add_control(spec, ALC_CTL_BIND_MUTE, name, >+ HDA_COMPOSE_AMP_VAL(nid_s, 3, 2, HDA_INPUT)); >+ if (err < 0) >+ return err; >+ } else if (alc880_is_multi_pin(pin)) { >+ /* set manual connection */ >+ /* we have only a switch on HP-out PIN */ >+ sprintf(name, "%s Playback Switch", pfx); >+ err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, >+ HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ } >+ return 0; >+} >+ >+/* 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 >+ * Based on ALC880 version - had to change it to override >+ * alc880_auto_create_extra_out and alc880_auto_create_multi_out_ctls */ >+static int alc861vd_parse_auto_config(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ int err; >+ static hda_nid_t alc861vd_ignore[] = { 0x1d, 0 }; >+ >+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, >+ alc861vd_ignore); >+ if (err < 0) >+ return err; >+ if (!spec->autocfg.line_outs) >+ return 0; /* can't find valid BIOS pin config */ >+ >+ err = alc880_auto_fill_dac_nids(spec, &spec->autocfg); >+ if (err < 0) >+ return err; >+ err = alc861vd_auto_create_multi_out_ctls(spec, &spec->autocfg); >+ if (err < 0) >+ return err; >+ err = alc861vd_auto_create_extra_out(spec, >+ spec->autocfg.speaker_pins[0], >+ "Speaker"); >+ if (err < 0) >+ return err; >+ err = alc861vd_auto_create_extra_out(spec, >+ spec->autocfg.hp_pins[0], >+ "Headphone"); >+ if (err < 0) >+ return err; >+ err = alc880_auto_create_analog_input_ctls(spec, &spec->autocfg); >+ if (err < 0) >+ return err; >+ >+ spec->multiout.max_channels = spec->multiout.num_dacs * 2; >+ >+ if (spec->autocfg.dig_out_pin) >+ spec->multiout.dig_out_nid = ALC861VD_DIGOUT_NID; >+ >+ if (spec->kctl_alloc) >+ spec->mixers[spec->num_mixers++] = spec->kctl_alloc; >+ >+ spec->init_verbs[spec->num_init_verbs++] >+ = alc861vd_volume_init_verbs; >+ >+ spec->num_mux_defs = 1; >+ spec->input_mux = &spec->private_imux; >+ >+ err = alc_auto_add_mic_boost(codec); >+ if (err < 0) >+ return err; >+ >+ return 1; >+} >+ >+/* additional initialization for auto-configuration model */ >+static void alc861vd_auto_init(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ alc861vd_auto_init_multi_out(codec); >+ alc861vd_auto_init_hp_out(codec); >+ alc861vd_auto_init_analog_input(codec); >+ if (spec->unsol_event) >+ alc_sku_automute(codec); >+} >+ >+static int patch_alc861vd(struct hda_codec *codec) >+{ >+ struct alc_spec *spec; >+ int err, board_config; >+ >+ spec = kzalloc(sizeof(*spec), GFP_KERNEL); >+ if (spec == NULL) >+ return -ENOMEM; >+ >+ codec->spec = spec; >+ >+ board_config = snd_hda_check_board_config(codec, ALC861VD_MODEL_LAST, >+ alc861vd_models, >+ alc861vd_cfg_tbl); >+ >+ if (board_config < 0 || board_config >= ALC861VD_MODEL_LAST) { >+ printk(KERN_INFO "hda_codec: Unknown model for ALC660VD/" >+ "ALC861VD, trying auto-probe from BIOS...\n"); >+ board_config = ALC861VD_AUTO; >+ } >+ >+ if (board_config == ALC861VD_AUTO) { >+ /* automatic parse from the BIOS config */ >+ err = alc861vd_parse_auto_config(codec); >+ if (err < 0) { >+ alc_free(codec); >+ return err; >+ } else if (!err) { >+ printk(KERN_INFO >+ "hda_codec: Cannot set up configuration " >+ "from BIOS. Using base mode...\n"); >+ board_config = ALC861VD_3ST; >+ } >+ } >+ >+ if (board_config != ALC861VD_AUTO) >+ setup_preset(spec, &alc861vd_presets[board_config]); >+ >+ if (codec->vendor_id == 0x10ec0660) { >+ spec->stream_name_analog = "ALC660-VD Analog"; >+ spec->stream_name_digital = "ALC660-VD Digital"; >+ /* always turn on EAPD */ >+ spec->init_verbs[spec->num_init_verbs++] = alc660vd_eapd_verbs; >+ } else { >+ spec->stream_name_analog = "ALC861VD Analog"; >+ spec->stream_name_digital = "ALC861VD Digital"; >+ } >+ >+ spec->stream_analog_playback = &alc861vd_pcm_analog_playback; >+ spec->stream_analog_capture = &alc861vd_pcm_analog_capture; >+ >+ spec->stream_digital_playback = &alc861vd_pcm_digital_playback; >+ spec->stream_digital_capture = &alc861vd_pcm_digital_capture; >+ >+ spec->adc_nids = alc861vd_adc_nids; >+ spec->num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids); >+ spec->capsrc_nids = alc861vd_capsrc_nids; >+ >+ spec->mixers[spec->num_mixers] = alc861vd_capture_mixer; >+ spec->num_mixers++; >+ >+ spec->vmaster_nid = 0x02; >+ >+ codec->patch_ops = alc_patch_ops; >+ >+ if (board_config == ALC861VD_AUTO) >+ spec->init_hook = alc861vd_auto_init; >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+ if (!spec->loopback.amplist) >+ spec->loopback.amplist = alc861vd_loopbacks; >+#endif >+ >+ return 0; >+} >+ >+/* >+ * 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). >+ */ >+#define ALC662_DIGOUT_NID 0x06 >+#define ALC662_DIGIN_NID 0x0a >+ >+static hda_nid_t alc662_dac_nids[4] = { >+ /* front, rear, clfe, rear_surr */ >+ 0x02, 0x03, 0x04 >+}; >+ >+static hda_nid_t alc662_adc_nids[1] = { >+ /* ADC1-2 */ >+ 0x09, >+}; >+ >+static hda_nid_t alc662_capsrc_nids[1] = { 0x22 }; >+ >+/* input MUX */ >+/* FIXME: should be a matrix-type input source selection */ >+static struct hda_input_mux alc662_capture_source = { >+ .num_items = 4, >+ .items = { >+ { "Mic", 0x0 }, >+ { "Front Mic", 0x1 }, >+ { "Line", 0x2 }, >+ { "CD", 0x4 }, >+ }, >+}; >+ >+static struct hda_input_mux alc662_lenovo_101e_capture_source = { >+ .num_items = 2, >+ .items = { >+ { "Mic", 0x1 }, >+ { "Line", 0x2 }, >+ }, >+}; >+ >+static struct hda_input_mux alc662_eeepc_capture_source = { >+ .num_items = 2, >+ .items = { >+ { "i-Mic", 0x1 }, >+ { "e-Mic", 0x0 }, >+ }, >+}; >+ >+static struct hda_input_mux alc663_capture_source = { >+ .num_items = 3, >+ .items = { >+ { "Mic", 0x0 }, >+ { "Front Mic", 0x1 }, >+ { "Line", 0x2 }, >+ }, >+}; >+ >+static struct hda_input_mux alc663_m51va_capture_source = { >+ .num_items = 2, >+ .items = { >+ { "Ext-Mic", 0x0 }, >+ { "D-Mic", 0x9 }, >+ }, >+}; >+ >+#define alc662_mux_enum_info alc_mux_enum_info >+#define alc662_mux_enum_get alc_mux_enum_get >+#define alc662_mux_enum_put alc882_mux_enum_put >+ >+/* >+ * 2ch mode >+ */ >+static struct hda_channel_mode alc662_3ST_2ch_modes[1] = { >+ { 2, NULL } >+}; >+ >+/* >+ * 2ch mode >+ */ >+static struct hda_verb alc662_3ST_ch2_init[] = { >+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, >+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, >+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, >+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, >+ { } /* end */ >+}; >+ >+/* >+ * 6ch mode >+ */ >+static struct hda_verb alc662_3ST_ch6_init[] = { >+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, >+ { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, >+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, >+ { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, >+ { } /* end */ >+}; >+ >+static struct hda_channel_mode alc662_3ST_6ch_modes[2] = { >+ { 2, alc662_3ST_ch2_init }, >+ { 6, alc662_3ST_ch6_init }, >+}; >+ >+/* >+ * 2ch mode >+ */ >+static struct hda_verb alc662_sixstack_ch6_init[] = { >+ { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, >+ { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, >+ { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { } /* end */ >+}; >+ >+/* >+ * 6ch mode >+ */ >+static struct hda_verb alc662_sixstack_ch8_init[] = { >+ { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, >+ { } /* end */ >+}; >+ >+static struct hda_channel_mode alc662_5stack_modes[2] = { >+ { 2, alc662_sixstack_ch6_init }, >+ { 6, alc662_sixstack_ch8_init }, >+}; >+ >+/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17 >+ * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b >+ */ >+ >+static struct snd_kcontrol_new alc662_base_mixer[] = { >+ /* output mixer control */ >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x3, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Surround Playback Switch", 0x0d, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), >+ >+ /*Input mixer control */ >+ HDA_CODEC_VOLUME("CD Playback Volume", 0xb, 0x4, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0xb, 0x4, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0xb, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0xb, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0xb, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0xb, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0xb, 0x01, HDA_INPUT), >+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0xb, 0x01, HDA_INPUT), >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc662_3ST_2ch_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), >+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc662_3ST_6ch_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Surround Playback Switch", 0x0d, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), >+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc662_lenovo_101e_mixer[] = { >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Front Playback Switch", 0x02, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x03, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Speaker Playback Switch", 0x03, 2, HDA_INPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc662_eeepc_p701_mixer[] = { >+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), >+ >+ HDA_CODEC_VOLUME("Line-Out Playback Volume", 0x02, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Line-Out Playback Switch", 0x1b, 0x0, HDA_OUTPUT), >+ >+ HDA_CODEC_VOLUME("e-Mic Boost", 0x18, 0, HDA_INPUT), >+ HDA_CODEC_VOLUME("e-Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("e-Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ >+ HDA_CODEC_VOLUME("i-Mic Boost", 0x19, 0, HDA_INPUT), >+ HDA_CODEC_VOLUME("i-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_MUTE("i-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc662_eeepc_ep20_mixer[] = { >+ HDA_CODEC_VOLUME("Line-Out Playback Volume", 0x02, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Line-Out Playback Switch", 0x14, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("Surround Playback Switch", 0x03, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x04, 1, 2, HDA_INPUT), >+ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x04, 2, 2, HDA_INPUT), >+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x1b, 0x0, HDA_OUTPUT), >+ HDA_BIND_MUTE("MuteCtrl Playback Switch", 0x0c, 2, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc663_m51va_mixer[] = { >+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("DMic Playback Switch", 0x23, 0x9, HDA_INPUT), >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc663_g71v_mixer[] = { >+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Front Playback Volume", 0x03, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT), >+ >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("i-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_MUTE("i-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc663_g50v_mixer[] = { >+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT), >+ >+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME("i-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_MUTE("i-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), >+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), >+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new alc662_chmode_mixer[] = { >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Channel Mode", >+ .info = alc_ch_mode_info, >+ .get = alc_ch_mode_get, >+ .put = alc_ch_mode_put, >+ }, >+ { } /* end */ >+}; >+ >+static struct hda_verb alc662_init_verbs[] = { >+ /* ADC: mute amp left and right */ >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ /* Front mixer: unmute input/output amp left and right (volume = 0) */ >+ >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, >+ >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ >+ /* Front Pin: output 0 (0x0c) */ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ >+ /* Rear Pin: output 1 (0x0d) */ >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ >+ /* CLFE Pin: output 2 (0x0e) */ >+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ >+ /* Mic (rear) pin: input vref at 80% */ >+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* Front Mic pin: input vref at 80% */ >+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* Line In pin: input */ >+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, >+ /* Line-2 In: Headphone output (output 0 - 0x0c) */ >+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ /* CD pin widget for input */ >+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, >+ >+ /* FIXME: use matrix-type input source selection */ >+ /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ >+ /* Input mixer */ >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, >+ >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, >+ >+ /* always trun on EAPD */ >+ {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, >+ {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, >+ >+ { } >+}; >+ >+static struct hda_verb alc662_sue_init_verbs[] = { >+ {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_FRONT_EVENT}, >+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_HP_EVENT}, >+ {} >+}; >+ >+static struct hda_verb alc662_eeepc_sue_init_verbs[] = { >+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, >+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, >+ {} >+}; >+ >+/* Set Unsolicited Event*/ >+static struct hda_verb alc662_eeepc_ep20_sue_init_verbs[] = { >+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, >+ {} >+}; >+ >+/* >+ * generic initialization of ADC, input mixers and output mixers >+ */ >+static struct hda_verb alc662_auto_init_verbs[] = { >+ /* >+ * Unmute ADC and set the default input to mic-in >+ */ >+ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ >+ /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback >+ * mixer widget >+ * Note: PASD motherboards uses the Line In 2 as the input for front >+ * panel mic (mic 2) >+ */ >+ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, >+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, >+ >+ /* >+ * Set up output mixers (0x0c - 0x0f) >+ */ >+ /* set vol=0 to output mixers */ >+ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ >+ /* set up input amps for analog loopback */ >+ /* Amp Indices: DAC = 0, mixer = 1 */ >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ >+ >+ /* FIXME: use matrix-type input source selection */ >+ /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ >+ /* Input mixer */ >+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ { } >+}; >+ >+static struct hda_verb alc663_m51va_init_verbs[] = { >+ {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x21, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Headphone */ >+ >+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)}, >+ >+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, >+ {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, >+ {} >+}; >+ >+static struct hda_verb alc663_g71v_init_verbs[] = { >+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ /* {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, */ >+ /* {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, */ /* Headphone */ >+ >+ {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x21, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Headphone */ >+ >+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_FRONT_EVENT}, >+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_MIC_EVENT}, >+ {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_HP_EVENT}, >+ {} >+}; >+ >+static struct hda_verb alc663_g50v_init_verbs[] = { >+ {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, >+ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, >+ {0x21, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Headphone */ >+ >+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, >+ {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, >+ {} >+}; >+ >+/* capture mixer elements */ >+static struct snd_kcontrol_new alc662_capture_mixer[] = { >+ HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ /* The multiple "Capture Source" controls confuse alsamixer >+ * So call somewhat different.. >+ */ >+ /* .name = "Capture Source", */ >+ .name = "Input Source", >+ .count = 1, >+ .info = alc662_mux_enum_info, >+ .get = alc662_mux_enum_get, >+ .put = alc662_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+static void alc662_lenovo_101e_ispeaker_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ unsigned char bits; >+ >+ present = snd_hda_codec_read(codec, 0x14, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ bits = present ? HDA_AMP_MUTE : 0; >+ snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, bits); >+} >+ >+static void alc662_lenovo_101e_all_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ unsigned char bits; >+ >+ present = snd_hda_codec_read(codec, 0x1b, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ bits = present ? HDA_AMP_MUTE : 0; >+ snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, bits); >+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, bits); >+} >+ >+static void alc662_lenovo_101e_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ if ((res >> 26) == ALC880_HP_EVENT) >+ alc662_lenovo_101e_all_automute(codec); >+ if ((res >> 26) == ALC880_FRONT_EVENT) >+ alc662_lenovo_101e_ispeaker_automute(codec); >+} >+ >+static void alc662_eeepc_mic_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ >+ present = snd_hda_codec_read(codec, 0x18, 0, >+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; >+ snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE, >+ 0x7000 | (0x00 << 8) | (present ? 0 : 0x80)); >+ snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE, >+ 0x7000 | (0x00 << 8) | (present ? 0 : 0x80)); >+ snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE, >+ 0x7000 | (0x01 << 8) | (present ? 0x80 : 0)); >+ snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE, >+ 0x7000 | (0x01 << 8) | (present ? 0x80 : 0)); >+} >+ >+/* unsolicited event for HP jack sensing */ >+static void alc662_eeepc_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ if ((res >> 26) == ALC880_HP_EVENT) >+ alc262_hippo1_automute( codec ); >+ >+ if ((res >> 26) == ALC880_MIC_EVENT) >+ alc662_eeepc_mic_automute(codec); >+} >+ >+static void alc662_eeepc_inithook(struct hda_codec *codec) >+{ >+ alc262_hippo1_automute( codec ); >+ alc662_eeepc_mic_automute(codec); >+} >+ >+static void alc662_eeepc_ep20_automute(struct hda_codec *codec) >+{ >+ unsigned int mute; >+ unsigned int present; >+ >+ snd_hda_codec_read(codec, 0x14, 0, AC_VERB_SET_PIN_SENSE, 0); >+ present = snd_hda_codec_read(codec, 0x14, 0, >+ AC_VERB_GET_PIN_SENSE, 0); >+ present = (present & 0x80000000) != 0; >+ if (present) { >+ /* mute internal speaker */ >+ snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, HDA_AMP_MUTE); >+ } else { >+ /* unmute internal speaker if necessary */ >+ mute = snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0); >+ snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, mute); >+ } >+} >+ >+/* unsolicited event for HP jack sensing */ >+static void alc662_eeepc_ep20_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ if ((res >> 26) == ALC880_HP_EVENT) >+ alc662_eeepc_ep20_automute(codec); >+} >+ >+static void alc662_eeepc_ep20_inithook(struct hda_codec *codec) >+{ >+ alc662_eeepc_ep20_automute(codec); >+} >+ >+static void alc663_m51va_speaker_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ unsigned char bits; >+ >+ present = snd_hda_codec_read(codec, 0x21, 0, >+ AC_VERB_GET_PIN_SENSE, 0) >+ & AC_PINSENSE_PRESENCE; >+ bits = present ? HDA_AMP_MUTE : 0; >+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, bits); >+} >+ >+static void alc663_m51va_mic_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ >+ present = snd_hda_codec_read(codec, 0x18, 0, >+ AC_VERB_GET_PIN_SENSE, 0) >+ & AC_PINSENSE_PRESENCE; >+ snd_hda_codec_write_cache(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE, >+ 0x7000 | (0x00 << 8) | (present ? 0 : 0x80)); >+ snd_hda_codec_write_cache(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE, >+ 0x7000 | (0x00 << 8) | (present ? 0 : 0x80)); >+ snd_hda_codec_write_cache(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE, >+ 0x7000 | (0x09 << 8) | (present ? 0x80 : 0)); >+ snd_hda_codec_write_cache(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE, >+ 0x7000 | (0x09 << 8) | (present ? 0x80 : 0)); >+} >+ >+static void alc663_m51va_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ switch (res >> 26) { >+ case ALC880_HP_EVENT: >+ alc663_m51va_speaker_automute(codec); >+ break; >+ case ALC880_MIC_EVENT: >+ alc663_m51va_mic_automute(codec); >+ break; >+ } >+} >+ >+static void alc663_m51va_inithook(struct hda_codec *codec) >+{ >+ alc663_m51va_speaker_automute(codec); >+ alc663_m51va_mic_automute(codec); >+} >+ >+static void alc663_g71v_hp_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ unsigned char bits; >+ >+ present = snd_hda_codec_read(codec, 0x21, 0, >+ AC_VERB_GET_PIN_SENSE, 0) >+ & AC_PINSENSE_PRESENCE; >+ bits = present ? HDA_AMP_MUTE : 0; >+ snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, bits); >+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, bits); >+} >+ >+static void alc663_g71v_front_automute(struct hda_codec *codec) >+{ >+ unsigned int present; >+ unsigned char bits; >+ >+ present = snd_hda_codec_read(codec, 0x15, 0, >+ AC_VERB_GET_PIN_SENSE, 0) >+ & AC_PINSENSE_PRESENCE; >+ bits = present ? HDA_AMP_MUTE : 0; >+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, >+ HDA_AMP_MUTE, bits); >+} >+ >+static void alc663_g71v_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ switch (res >> 26) { >+ case ALC880_HP_EVENT: >+ alc663_g71v_hp_automute(codec); >+ break; >+ case ALC880_FRONT_EVENT: >+ alc663_g71v_front_automute(codec); >+ break; >+ case ALC880_MIC_EVENT: >+ alc662_eeepc_mic_automute(codec); >+ break; >+ } >+} >+ >+static void alc663_g71v_inithook(struct hda_codec *codec) >+{ >+ alc663_g71v_front_automute(codec); >+ alc663_g71v_hp_automute(codec); >+ alc662_eeepc_mic_automute(codec); >+} >+ >+static void alc663_g50v_unsol_event(struct hda_codec *codec, >+ unsigned int res) >+{ >+ switch (res >> 26) { >+ case ALC880_HP_EVENT: >+ alc663_m51va_speaker_automute(codec); >+ break; >+ case ALC880_MIC_EVENT: >+ alc662_eeepc_mic_automute(codec); >+ break; >+ } >+} >+ >+static void alc663_g50v_inithook(struct hda_codec *codec) >+{ >+ alc663_m51va_speaker_automute(codec); >+ alc662_eeepc_mic_automute(codec); >+} >+ >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+#define alc662_loopbacks alc880_loopbacks >+#endif >+ >+ >+/* pcm configuration: identiacal with ALC880 */ >+#define alc662_pcm_analog_playback alc880_pcm_analog_playback >+#define alc662_pcm_analog_capture alc880_pcm_analog_capture >+#define alc662_pcm_digital_playback alc880_pcm_digital_playback >+#define alc662_pcm_digital_capture alc880_pcm_digital_capture >+ >+/* >+ * configuration and preset >+ */ >+static const char *alc662_models[ALC662_MODEL_LAST] = { >+ [ALC662_3ST_2ch_DIG] = "3stack-dig", >+ [ALC662_3ST_6ch_DIG] = "3stack-6ch-dig", >+ [ALC662_3ST_6ch] = "3stack-6ch", >+ [ALC662_5ST_DIG] = "6stack-dig", >+ [ALC662_LENOVO_101E] = "lenovo-101e", >+ [ALC662_ASUS_EEEPC_P701] = "eeepc-p701", >+ [ALC662_ASUS_EEEPC_EP20] = "eeepc-ep20", >+ [ALC663_ASUS_M51VA] = "m51va", >+ [ALC663_ASUS_G71V] = "g71v", >+ [ALC663_ASUS_H13] = "h13", >+ [ALC663_ASUS_G50V] = "g50v", >+ [ALC662_AUTO] = "auto", >+}; >+ >+static struct snd_pci_quirk alc662_cfg_tbl[] = { >+ SND_PCI_QUIRK(0x1043, 0x11c3, "ASUS G71V", ALC663_ASUS_G71V), >+ SND_PCI_QUIRK(0x1043, 0x1878, "ASUS M51VA", ALC663_ASUS_M51VA), >+ SND_PCI_QUIRK(0x1043, 0x19a3, "ASUS M51VA", ALC663_ASUS_G50V), >+ SND_PCI_QUIRK(0x1043, 0x8290, "ASUS P5GC-MX", ALC662_3ST_6ch_DIG), >+ SND_PCI_QUIRK(0x1043, 0x82a1, "ASUS Eeepc", ALC662_ASUS_EEEPC_P701), >+ SND_PCI_QUIRK(0x1043, 0x82d1, "ASUS Eeepc EP20", ALC662_ASUS_EEEPC_EP20), >+ SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E), >+ SND_PCI_QUIRK(0x1854, 0x2000, "ASUS H13-2000", ALC663_ASUS_H13), >+ SND_PCI_QUIRK(0x1854, 0x2001, "ASUS H13-2001", ALC663_ASUS_H13), >+ SND_PCI_QUIRK(0x1854, 0x2002, "ASUS H13-2002", ALC663_ASUS_H13), >+ {} >+}; >+ >+static struct alc_config_preset alc662_presets[] = { >+ [ALC662_3ST_2ch_DIG] = { >+ .mixers = { alc662_3ST_2ch_mixer, alc662_capture_mixer }, >+ .init_verbs = { alc662_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc662_dac_nids), >+ .dac_nids = alc662_dac_nids, >+ .dig_out_nid = ALC662_DIGOUT_NID, >+ .dig_in_nid = ALC662_DIGIN_NID, >+ .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), >+ .channel_mode = alc662_3ST_2ch_modes, >+ .input_mux = &alc662_capture_source, >+ }, >+ [ALC662_3ST_6ch_DIG] = { >+ .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer, >+ alc662_capture_mixer }, >+ .init_verbs = { alc662_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc662_dac_nids), >+ .dac_nids = alc662_dac_nids, >+ .dig_out_nid = ALC662_DIGOUT_NID, >+ .dig_in_nid = ALC662_DIGIN_NID, >+ .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), >+ .channel_mode = alc662_3ST_6ch_modes, >+ .need_dac_fix = 1, >+ .input_mux = &alc662_capture_source, >+ }, >+ [ALC662_3ST_6ch] = { >+ .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer, >+ alc662_capture_mixer }, >+ .init_verbs = { alc662_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc662_dac_nids), >+ .dac_nids = alc662_dac_nids, >+ .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), >+ .channel_mode = alc662_3ST_6ch_modes, >+ .need_dac_fix = 1, >+ .input_mux = &alc662_capture_source, >+ }, >+ [ALC662_5ST_DIG] = { >+ .mixers = { alc662_base_mixer, alc662_chmode_mixer, >+ alc662_capture_mixer }, >+ .init_verbs = { alc662_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc662_dac_nids), >+ .dac_nids = alc662_dac_nids, >+ .dig_out_nid = ALC662_DIGOUT_NID, >+ .dig_in_nid = ALC662_DIGIN_NID, >+ .num_channel_mode = ARRAY_SIZE(alc662_5stack_modes), >+ .channel_mode = alc662_5stack_modes, >+ .input_mux = &alc662_capture_source, >+ }, >+ [ALC662_LENOVO_101E] = { >+ .mixers = { alc662_lenovo_101e_mixer, alc662_capture_mixer }, >+ .init_verbs = { alc662_init_verbs, alc662_sue_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc662_dac_nids), >+ .dac_nids = alc662_dac_nids, >+ .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), >+ .channel_mode = alc662_3ST_2ch_modes, >+ .input_mux = &alc662_lenovo_101e_capture_source, >+ .unsol_event = alc662_lenovo_101e_unsol_event, >+ .init_hook = alc662_lenovo_101e_all_automute, >+ }, >+ [ALC662_ASUS_EEEPC_P701] = { >+ .mixers = { alc662_eeepc_p701_mixer, alc662_capture_mixer }, >+ .init_verbs = { alc662_init_verbs, >+ alc662_eeepc_sue_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc662_dac_nids), >+ .dac_nids = alc662_dac_nids, >+ .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), >+ .channel_mode = alc662_3ST_2ch_modes, >+ .input_mux = &alc662_eeepc_capture_source, >+ .unsol_event = alc662_eeepc_unsol_event, >+ .init_hook = alc662_eeepc_inithook, >+ }, >+ [ALC662_ASUS_EEEPC_EP20] = { >+ .mixers = { alc662_eeepc_ep20_mixer, alc662_capture_mixer, >+ alc662_chmode_mixer }, >+ .init_verbs = { alc662_init_verbs, >+ alc662_eeepc_ep20_sue_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc662_dac_nids), >+ .dac_nids = alc662_dac_nids, >+ .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), >+ .channel_mode = alc662_3ST_6ch_modes, >+ .input_mux = &alc662_lenovo_101e_capture_source, >+ .unsol_event = alc662_eeepc_ep20_unsol_event, >+ .init_hook = alc662_eeepc_ep20_inithook, >+ }, >+ [ALC663_ASUS_M51VA] = { >+ .mixers = { alc663_m51va_mixer, alc662_capture_mixer}, >+ .init_verbs = { alc662_init_verbs, alc663_m51va_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc662_dac_nids), >+ .dac_nids = alc662_dac_nids, >+ .dig_out_nid = ALC662_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), >+ .channel_mode = alc662_3ST_2ch_modes, >+ .input_mux = &alc663_m51va_capture_source, >+ .unsol_event = alc663_m51va_unsol_event, >+ .init_hook = alc663_m51va_inithook, >+ }, >+ [ALC663_ASUS_G71V] = { >+ .mixers = { alc663_g71v_mixer, alc662_capture_mixer}, >+ .init_verbs = { alc662_init_verbs, alc663_g71v_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc662_dac_nids), >+ .dac_nids = alc662_dac_nids, >+ .dig_out_nid = ALC662_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), >+ .channel_mode = alc662_3ST_2ch_modes, >+ .input_mux = &alc662_eeepc_capture_source, >+ .unsol_event = alc663_g71v_unsol_event, >+ .init_hook = alc663_g71v_inithook, >+ }, >+ [ALC663_ASUS_H13] = { >+ .mixers = { alc663_m51va_mixer, alc662_capture_mixer}, >+ .init_verbs = { alc662_init_verbs, alc663_m51va_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc662_dac_nids), >+ .dac_nids = alc662_dac_nids, >+ .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), >+ .channel_mode = alc662_3ST_2ch_modes, >+ .input_mux = &alc663_m51va_capture_source, >+ .unsol_event = alc663_m51va_unsol_event, >+ .init_hook = alc663_m51va_inithook, >+ }, >+ [ALC663_ASUS_G50V] = { >+ .mixers = { alc663_g50v_mixer, alc662_capture_mixer}, >+ .init_verbs = { alc662_init_verbs, alc663_g50v_init_verbs }, >+ .num_dacs = ARRAY_SIZE(alc662_dac_nids), >+ .dac_nids = alc662_dac_nids, >+ .dig_out_nid = ALC662_DIGOUT_NID, >+ .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), >+ .channel_mode = alc662_3ST_6ch_modes, >+ .input_mux = &alc663_capture_source, >+ .unsol_event = alc663_g50v_unsol_event, >+ .init_hook = alc663_g50v_inithook, >+ }, >+}; >+ >+ >+/* >+ * BIOS auto configuration >+ */ >+ >+/* add playback controls from the parsed DAC table */ >+static int alc662_auto_create_multi_out_ctls(struct alc_spec *spec, >+ const struct auto_pin_cfg *cfg) >+{ >+ char name[32]; >+ static const char *chname[4] = { >+ "Front", "Surround", NULL /*CLFE*/, "Side" >+ }; >+ hda_nid_t nid; >+ int i, err; >+ >+ for (i = 0; i < cfg->line_outs; i++) { >+ if (!spec->multiout.dac_nids[i]) >+ continue; >+ nid = alc880_idx_to_dac(i); >+ if (i == 2) { >+ /* Center/LFE */ >+ err = add_control(spec, ALC_CTL_WIDGET_VOL, >+ "Center Playback Volume", >+ HDA_COMPOSE_AMP_VAL(nid, 1, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ err = add_control(spec, ALC_CTL_WIDGET_VOL, >+ "LFE Playback Volume", >+ HDA_COMPOSE_AMP_VAL(nid, 2, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ err = add_control(spec, ALC_CTL_BIND_MUTE, >+ "Center Playback Switch", >+ HDA_COMPOSE_AMP_VAL(nid, 1, 2, >+ HDA_INPUT)); >+ if (err < 0) >+ return err; >+ err = add_control(spec, ALC_CTL_BIND_MUTE, >+ "LFE Playback Switch", >+ 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, ALC_CTL_WIDGET_VOL, name, >+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ sprintf(name, "%s Playback Switch", chname[i]); >+ err = add_control(spec, ALC_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 alc662_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin, >+ const char *pfx) >+{ >+ hda_nid_t nid; >+ int err; >+ char name[32]; >+ >+ if (!pin) >+ return 0; >+ >+ if (alc880_is_fixed_pin(pin)) { >+ nid = alc880_idx_to_dac(alc880_fixed_pin_idx(pin)); >+ /* printk("DAC nid=%x\n",nid); */ >+ /* 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 */ >+ nid = alc880_idx_to_dac(alc880_fixed_pin_idx(pin)); >+ sprintf(name, "%s Playback Volume", pfx); >+ err = add_control(spec, ALC_CTL_WIDGET_VOL, name, >+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ sprintf(name, "%s Playback Switch", pfx); >+ err = add_control(spec, ALC_CTL_BIND_MUTE, name, >+ HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT)); >+ if (err < 0) >+ return err; >+ } else if (alc880_is_multi_pin(pin)) { >+ /* set manual connection */ >+ /* we have only a switch on HP-out PIN */ >+ sprintf(name, "%s Playback Switch", pfx); >+ err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, >+ HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ } >+ return 0; >+} >+ >+/* create playback/capture controls for input pins */ >+static int alc662_auto_create_analog_input_ctls(struct alc_spec *spec, >+ const struct auto_pin_cfg *cfg) >+{ >+ struct hda_input_mux *imux = &spec->private_imux; >+ int i, err, idx; >+ >+ for (i = 0; i < AUTO_PIN_LAST; i++) { >+ if (alc880_is_input_pin(cfg->input_pins[i])) { >+ idx = alc880_input_pin_idx(cfg->input_pins[i]); >+ err = new_analog_input(spec, cfg->input_pins[i], >+ auto_pin_cfg_labels[i], >+ idx, 0x0b); >+ if (err < 0) >+ return err; >+ imux->items[imux->num_items].label = >+ auto_pin_cfg_labels[i]; >+ imux->items[imux->num_items].index = >+ alc880_input_pin_idx(cfg->input_pins[i]); >+ imux->num_items++; >+ } >+ } >+ return 0; >+} >+ >+static void alc662_auto_set_output_and_unmute(struct hda_codec *codec, >+ hda_nid_t nid, int pin_type, >+ int dac_idx) >+{ >+ alc_set_pin_output(codec, nid, pin_type); >+ /* need the manual connection? */ >+ if (alc880_is_multi_pin(nid)) { >+ struct alc_spec *spec = codec->spec; >+ int idx = alc880_multi_pin_idx(nid); >+ snd_hda_codec_write(codec, alc880_idx_to_selector(idx), 0, >+ AC_VERB_SET_CONNECT_SEL, >+ alc880_dac_to_idx(spec->multiout.dac_nids[dac_idx])); >+ } >+} >+ >+static void alc662_auto_init_multi_out(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ int i; >+ >+ alc_subsystem_id(codec, 0x15, 0x1b, 0x14); >+ for (i = 0; i <= HDA_SIDE; i++) { >+ hda_nid_t nid = spec->autocfg.line_out_pins[i]; >+ int pin_type = get_pin_type(spec->autocfg.line_out_type); >+ if (nid) >+ alc662_auto_set_output_and_unmute(codec, nid, pin_type, >+ i); >+ } >+} >+ >+static void alc662_auto_init_hp_out(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ hda_nid_t pin; >+ >+ pin = spec->autocfg.hp_pins[0]; >+ if (pin) /* connect to front */ >+ /* use dac 0 */ >+ alc662_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); >+ pin = spec->autocfg.speaker_pins[0]; >+ if (pin) >+ alc662_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); >+} >+ >+#define alc662_is_input_pin(nid) alc880_is_input_pin(nid) >+#define ALC662_PIN_CD_NID ALC880_PIN_CD_NID >+ >+static void alc662_auto_init_analog_input(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ int i; >+ >+ for (i = 0; i < AUTO_PIN_LAST; i++) { >+ hda_nid_t nid = spec->autocfg.input_pins[i]; >+ if (alc662_is_input_pin(nid)) { >+ snd_hda_codec_write(codec, nid, 0, >+ AC_VERB_SET_PIN_WIDGET_CONTROL, >+ (i <= AUTO_PIN_FRONT_MIC ? >+ PIN_VREF80 : PIN_IN)); >+ if (nid != ALC662_PIN_CD_NID) >+ snd_hda_codec_write(codec, nid, 0, >+ AC_VERB_SET_AMP_GAIN_MUTE, >+ AMP_OUT_MUTE); >+ } >+ } >+} >+ >+static int alc662_parse_auto_config(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ int err; >+ static hda_nid_t alc662_ignore[] = { 0x1d, 0 }; >+ >+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, >+ alc662_ignore); >+ if (err < 0) >+ return err; >+ if (!spec->autocfg.line_outs) >+ return 0; /* can't find valid BIOS pin config */ >+ >+ err = alc880_auto_fill_dac_nids(spec, &spec->autocfg); >+ if (err < 0) >+ return err; >+ err = alc662_auto_create_multi_out_ctls(spec, &spec->autocfg); >+ if (err < 0) >+ return err; >+ err = alc662_auto_create_extra_out(spec, >+ spec->autocfg.speaker_pins[0], >+ "Speaker"); >+ if (err < 0) >+ return err; >+ err = alc662_auto_create_extra_out(spec, spec->autocfg.hp_pins[0], >+ "Headphone"); >+ if (err < 0) >+ return err; >+ err = alc662_auto_create_analog_input_ctls(spec, &spec->autocfg); >+ if (err < 0) >+ return err; >+ >+ spec->multiout.max_channels = spec->multiout.num_dacs * 2; >+ >+ if (spec->autocfg.dig_out_pin) >+ spec->multiout.dig_out_nid = ALC880_DIGOUT_NID; >+ >+ if (spec->kctl_alloc) >+ spec->mixers[spec->num_mixers++] = spec->kctl_alloc; >+ >+ spec->num_mux_defs = 1; >+ spec->input_mux = &spec->private_imux; >+ >+ spec->init_verbs[spec->num_init_verbs++] = alc662_auto_init_verbs; >+ spec->mixers[spec->num_mixers] = alc662_capture_mixer; >+ spec->num_mixers++; >+ return 1; >+} >+ >+/* additional initialization for auto-configuration model */ >+static void alc662_auto_init(struct hda_codec *codec) >+{ >+ struct alc_spec *spec = codec->spec; >+ alc662_auto_init_multi_out(codec); >+ alc662_auto_init_hp_out(codec); >+ alc662_auto_init_analog_input(codec); >+ if (spec->unsol_event) >+ alc_sku_automute(codec); >+} >+ >+static int patch_alc662(struct hda_codec *codec) >+{ >+ struct alc_spec *spec; >+ int err, board_config; >+ >+ spec = kzalloc(sizeof(*spec), GFP_KERNEL); >+ if (!spec) >+ return -ENOMEM; >+ >+ codec->spec = spec; >+ >+ alc_fix_pll_init(codec, 0x20, 0x04, 15); >+ >+ board_config = snd_hda_check_board_config(codec, ALC662_MODEL_LAST, >+ alc662_models, >+ alc662_cfg_tbl); >+ if (board_config < 0) { >+ printk(KERN_INFO "hda_codec: Unknown model for ALC662, " >+ "trying auto-probe from BIOS...\n"); >+ board_config = ALC662_AUTO; >+ } >+ >+ if (board_config == ALC662_AUTO) { >+ /* automatic parse from the BIOS config */ >+ err = alc662_parse_auto_config(codec); >+ if (err < 0) { >+ alc_free(codec); >+ return err; >+ } else if (!err) { >+ printk(KERN_INFO >+ "hda_codec: Cannot set up configuration " >+ "from BIOS. Using base mode...\n"); >+ board_config = ALC662_3ST_2ch_DIG; >+ } >+ } >+ >+ if (board_config != ALC662_AUTO) >+ setup_preset(spec, &alc662_presets[board_config]); >+ >+ if (codec->vendor_id == 0x10ec0663) { >+ spec->stream_name_analog = "ALC663 Analog"; >+ spec->stream_name_digital = "ALC663 Digital"; >+ } else { >+ spec->stream_name_analog = "ALC662 Analog"; >+ spec->stream_name_digital = "ALC662 Digital"; >+ } >+ >+ spec->stream_analog_playback = &alc662_pcm_analog_playback; >+ spec->stream_analog_capture = &alc662_pcm_analog_capture; >+ >+ spec->stream_digital_playback = &alc662_pcm_digital_playback; >+ spec->stream_digital_capture = &alc662_pcm_digital_capture; >+ >+ spec->adc_nids = alc662_adc_nids; >+ spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids); >+ spec->capsrc_nids = alc662_capsrc_nids; >+ >+ spec->vmaster_nid = 0x02; >+ >+ codec->patch_ops = alc_patch_ops; >+ if (board_config == ALC662_AUTO) >+ spec->init_hook = alc662_auto_init; >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+ if (!spec->loopback.amplist) >+ spec->loopback.amplist = alc662_loopbacks; >+#endif >+ >+ return 0; >+} >+ >+/* >+ * patch entries >+ */ >+struct hda_codec_preset snd_hda_preset_realtek[] = { >+ { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 }, >+ { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 }, >+ { .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 }, >+ { .id = 0x10ec0268, .name = "ALC268", .patch = patch_alc268 }, >+ { .id = 0x10ec0269, .name = "ALC269", .patch = patch_alc269 }, >+ { .id = 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_alc883 }, >+ { .id = 0x10ec0662, .rev = 0x100101, .name = "ALC662 rev1", >+ .patch = patch_alc662 }, >+ { .id = 0x10ec0663, .name = "ALC663", .patch = patch_alc662 }, >+ { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 }, >+ { .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 }, >+ { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc883 }, >+ { .id = 0x10ec0885, .rev = 0x100103, .name = "ALC889A", >+ .patch = patch_alc882 }, /* should be patch_alc883() in future */ >+ { .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 }, >+ { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc883 }, >+ { .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc883 }, >+ {} /* terminator */ >+}; >diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c >new file mode 100644 >index 0000000..ceb3d01 >--- /dev/null >+++ b/sound/pci/hda/patch_si3054.c >@@ -0,0 +1,304 @@ >+/* >+ * 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 <sound/driver.h> >+#include <linux/init.h> >+#include <linux/delay.h> >+#include <linux/slab.h> >+#include <sound/core.h> >+#include "hda_codec.h" >+#include "hda_local.h" >+#include "hda_patch.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, \ >+ .info = si3054_switch_info, \ >+ .get = si3054_switch_get, \ >+ .put = si3054_switch_put, \ >+ .private_value = PRIVATE_VALUE(reg,mask), \ >+} >+ >+ >+static 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 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; >+ si3054_pcm.nid = codec->mfg; >+ 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->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 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 >+ */ >+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 }, >+ {} >+}; >+ >diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c >new file mode 100644 >index 0000000..f713e1b >--- /dev/null >+++ b/sound/pci/hda/patch_sigmatel.c >@@ -0,0 +1,4323 @@ >+/* >+ * 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 <sound/driver.h> >+#include <linux/init.h> >+#include <linux/delay.h> >+#include <linux/slab.h> >+#include <linux/pci.h> >+#include <sound/core.h> >+#include <sound/asoundef.h> >+#include "hda_codec.h" >+#include "hda_local.h" >+#include "hda_patch.h" >+ >+#define NUM_CONTROL_ALLOC 32 >+#define STAC_PWR_EVENT 0x20 >+#define STAC_HP_EVENT 0x30 >+ >+enum { >+ 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_GATEWAY, >+ STAC_9200_PANASONIC, >+ STAC_9200_MODELS >+}; >+ >+enum { >+ STAC_9205_REF, >+ STAC_9205_DELL_M42, >+ STAC_9205_DELL_M43, >+ STAC_9205_DELL_M44, >+ STAC_9205_MODELS >+}; >+ >+enum { >+ STAC_92HD73XX_REF, >+ STAC_DELL_M6, >+ STAC_92HD73XX_MODELS >+}; >+ >+enum { >+ STAC_92HD71BXX_REF, >+ STAC_DELL_M4_1, >+ STAC_DELL_M4_2, >+ STAC_92HD71BXX_MODELS >+}; >+ >+enum { >+ STAC_925x_REF, >+ STAC_M2_2, >+ STAC_MA6, >+ STAC_PA6, >+ STAC_925x_MODELS >+}; >+ >+enum { >+ 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, >+ /* for backward compatibility */ >+ STAC_MACMINI, >+ STAC_MACBOOK, >+ STAC_MACBOOK_PRO_V1, >+ STAC_MACBOOK_PRO_V2, >+ STAC_IMAC_INTEL, >+ STAC_IMAC_INTEL_20, >+ STAC_922X_DELL_D81, >+ STAC_922X_DELL_D82, >+ STAC_922X_DELL_M81, >+ STAC_922X_DELL_M82, >+ STAC_922X_MODELS >+}; >+ >+enum { >+ STAC_D965_REF, >+ STAC_D965_3ST, >+ STAC_D965_5ST, >+ STAC_DELL_3ST, >+ STAC_DELL_BIOS, >+ STAC_927X_MODELS >+}; >+ >+struct sigmatel_spec { >+ struct snd_kcontrol_new *mixers[4]; >+ unsigned int num_mixers; >+ >+ int board_config; >+ unsigned int surr_switch: 1; >+ unsigned int line_switch: 1; >+ unsigned int mic_switch: 1; >+ unsigned int alt_switch: 1; >+ unsigned int hp_detect: 1; >+ >+ /* gpio lines */ >+ unsigned int eapd_mask; >+ unsigned int gpio_mask; >+ unsigned int gpio_dir; >+ unsigned int gpio_data; >+ unsigned int gpio_mute; >+ >+ /* analog loopback */ >+ unsigned char aloopback_mask; >+ unsigned char aloopback_shift; >+ >+ /* power management */ >+ unsigned int num_pwrs; >+ hda_nid_t *pwr_nids; >+ 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]; >+ >+ /* capture */ >+ hda_nid_t *adc_nids; >+ unsigned int num_adcs; >+ hda_nid_t *mux_nids; >+ unsigned int num_muxes; >+ hda_nid_t *dmic_nids; >+ unsigned int num_dmics; >+ hda_nid_t *dmux_nids; >+ unsigned int num_dmuxes; >+ hda_nid_t dig_in_nid; >+ hda_nid_t mono_nid; >+ >+ /* pin widgets */ >+ hda_nid_t *pin_nids; >+ unsigned int num_pins; >+ unsigned int *pin_configs; >+ unsigned int *bios_pin_configs; >+ >+ /* codec specific stuff */ >+ struct hda_verb *init; >+ 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]; >+ >+ /* i/o switches */ >+ unsigned int io_switch[2]; >+ unsigned int clfe_swap; >+ unsigned int hp_switch; >+ unsigned int aloopback; >+ >+ struct hda_pcm pcm_rec[2]; /* PCM information */ >+ >+ /* dynamic controls and input_mux */ >+ struct auto_pin_cfg autocfg; >+ unsigned int num_kctl_alloc, num_kctl_used; >+ struct snd_kcontrol_new *kctl_alloc; >+ struct hda_input_mux private_dimux; >+ struct hda_input_mux private_imux; >+ struct hda_input_mux private_mono_mux; >+}; >+ >+static hda_nid_t stac9200_adc_nids[1] = { >+ 0x03, >+}; >+ >+static hda_nid_t stac9200_mux_nids[1] = { >+ 0x0c, >+}; >+ >+static hda_nid_t stac9200_dac_nids[1] = { >+ 0x02, >+}; >+ >+static hda_nid_t stac92hd73xx_pwr_nids[8] = { >+ 0x0a, 0x0b, 0x0c, 0xd, 0x0e, >+ 0x0f, 0x10, 0x11 >+}; >+ >+static hda_nid_t stac92hd73xx_adc_nids[2] = { >+ 0x1a, 0x1b >+}; >+ >+#define STAC92HD73XX_NUM_DMICS 2 >+static hda_nid_t stac92hd73xx_dmic_nids[STAC92HD73XX_NUM_DMICS + 1] = { >+ 0x13, 0x14, 0 >+}; >+ >+#define STAC92HD73_DAC_COUNT 5 >+static hda_nid_t stac92hd73xx_dac_nids[STAC92HD73_DAC_COUNT] = { >+ 0x15, 0x16, 0x17, 0x18, 0x19, >+}; >+ >+static hda_nid_t stac92hd73xx_mux_nids[4] = { >+ 0x28, 0x29, 0x2a, 0x2b, >+}; >+ >+static hda_nid_t stac92hd73xx_dmux_nids[2] = { >+ 0x20, 0x21, >+}; >+ >+static hda_nid_t stac92hd71bxx_pwr_nids[3] = { >+ 0x0a, 0x0d, 0x0f >+}; >+ >+static hda_nid_t stac92hd71bxx_adc_nids[2] = { >+ 0x12, 0x13, >+}; >+ >+static hda_nid_t stac92hd71bxx_mux_nids[2] = { >+ 0x1a, 0x1b >+}; >+ >+static hda_nid_t stac92hd71bxx_dmux_nids[1] = { >+ 0x1c, >+}; >+ >+static hda_nid_t stac92hd71bxx_dac_nids[1] = { >+ 0x10, /*0x11, */ >+}; >+ >+#define STAC92HD71BXX_NUM_DMICS 2 >+static hda_nid_t stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS + 1] = { >+ 0x18, 0x19, 0 >+}; >+ >+static hda_nid_t stac925x_adc_nids[1] = { >+ 0x03, >+}; >+ >+static hda_nid_t stac925x_mux_nids[1] = { >+ 0x0f, >+}; >+ >+static hda_nid_t stac925x_dac_nids[1] = { >+ 0x02, >+}; >+ >+#define STAC925X_NUM_DMICS 1 >+static hda_nid_t stac925x_dmic_nids[STAC925X_NUM_DMICS + 1] = { >+ 0x15, 0 >+}; >+ >+static hda_nid_t stac925x_dmux_nids[1] = { >+ 0x14, >+}; >+ >+static hda_nid_t stac922x_adc_nids[2] = { >+ 0x06, 0x07, >+}; >+ >+static hda_nid_t stac922x_mux_nids[2] = { >+ 0x12, 0x13, >+}; >+ >+static hda_nid_t stac927x_adc_nids[3] = { >+ 0x07, 0x08, 0x09 >+}; >+ >+static hda_nid_t stac927x_mux_nids[3] = { >+ 0x15, 0x16, 0x17 >+}; >+ >+static hda_nid_t stac927x_dac_nids[6] = { >+ 0x02, 0x03, 0x04, 0x05, 0x06, 0 >+}; >+ >+static hda_nid_t stac927x_dmux_nids[1] = { >+ 0x1b, >+}; >+ >+#define STAC927X_NUM_DMICS 2 >+static hda_nid_t stac927x_dmic_nids[STAC927X_NUM_DMICS + 1] = { >+ 0x13, 0x14, 0 >+}; >+ >+static hda_nid_t stac9205_adc_nids[2] = { >+ 0x12, 0x13 >+}; >+ >+static hda_nid_t stac9205_mux_nids[2] = { >+ 0x19, 0x1a >+}; >+ >+static hda_nid_t stac9205_dmux_nids[1] = { >+ 0x1d, >+}; >+ >+#define STAC9205_NUM_DMICS 2 >+static hda_nid_t stac9205_dmic_nids[STAC9205_NUM_DMICS + 1] = { >+ 0x17, 0x18, 0 >+}; >+ >+static hda_nid_t stac9200_pin_nids[8] = { >+ 0x08, 0x09, 0x0d, 0x0e, >+ 0x0f, 0x10, 0x11, 0x12, >+}; >+ >+static hda_nid_t stac925x_pin_nids[8] = { >+ 0x07, 0x08, 0x0a, 0x0b, >+ 0x0c, 0x0d, 0x10, 0x11, >+}; >+ >+static hda_nid_t stac922x_pin_nids[10] = { >+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, >+ 0x0f, 0x10, 0x11, 0x15, 0x1b, >+}; >+ >+static hda_nid_t stac92hd73xx_pin_nids[13] = { >+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, >+ 0x0f, 0x10, 0x11, 0x12, 0x13, >+ 0x14, 0x1e, 0x22 >+}; >+ >+static hda_nid_t stac92hd71bxx_pin_nids[10] = { >+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, >+ 0x0f, 0x14, 0x18, 0x19, 0x1e, >+}; >+ >+static hda_nid_t stac927x_pin_nids[14] = { >+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, >+ 0x0f, 0x10, 0x11, 0x12, 0x13, >+ 0x14, 0x21, 0x22, 0x23, >+}; >+ >+static 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_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); >+ >+ return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, >+ spec->mux_nids[adc_idx], &spec->cur_mux[adc_idx]); >+} >+ >+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 struct hda_verb stac9200_core_init[] = { >+ /* set dac0mux for dac converter */ >+ { 0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {} >+}; >+ >+static 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 struct hda_verb stac92hd73xx_6ch_core_init[] = { >+ /* set master volume and direct control */ >+ { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, >+ /* setup audio connections */ >+ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02}, >+ /* setup adcs to point to mixer */ >+ { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b}, >+ { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b}, >+ { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ /* setup import muxs */ >+ { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {} >+}; >+ >+static struct hda_verb dell_eq_core_init[] = { >+ /* set master volume to max value without distortion >+ * and direct control */ >+ { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xec}, >+ /* setup audio connections */ >+ { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x02}, >+ /* setup adcs to point to mixer */ >+ { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b}, >+ { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b}, >+ /* setup import muxs */ >+ { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {} >+}; >+ >+static struct hda_verb dell_m6_core_init[] = { >+ /* set master volume and direct control */ >+ { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, >+ /* setup audio connections */ >+ { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x02}, >+ /* setup adcs to point to mixer */ >+ { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b}, >+ { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b}, >+ /* setup import muxs */ >+ { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {} >+}; >+ >+static struct hda_verb stac92hd73xx_8ch_core_init[] = { >+ /* set master volume and direct control */ >+ { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, >+ /* setup audio connections */ >+ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02}, >+ /* connect hp ports to dac3 */ >+ { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x03}, >+ { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x03}, >+ /* setup adcs to point to mixer */ >+ { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b}, >+ { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b}, >+ { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ /* setup import muxs */ >+ { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x03}, >+ {} >+}; >+ >+static struct hda_verb stac92hd73xx_10ch_core_init[] = { >+ /* set master volume and direct control */ >+ { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, >+ /* setup audio connections */ >+ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, >+ { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01 }, >+ { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02 }, >+ /* dac3 is connected to import3 mux */ >+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb07f}, >+ /* connect hp ports to dac4 */ >+ { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x04}, >+ { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x04}, >+ /* setup adcs to point to mixer */ >+ { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b}, >+ { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b}, >+ { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, >+ /* setup import muxs */ >+ { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x03}, >+ {} >+}; >+ >+static struct hda_verb stac92hd71bxx_core_init[] = { >+ /* set master volume and direct control */ >+ { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, >+ /* connect headphone jack to dac1 */ >+ { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Speaker */ >+ /* unmute right and left channels for nodes 0x0a, 0xd, 0x0f */ >+ { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+}; >+ >+static struct hda_verb stac92hd71bxx_analog_core_init[] = { >+ /* set master volume and direct control */ >+ { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, >+ /* connect headphone jack to dac1 */ >+ { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01}, >+ /* connect ports 0d and 0f to audio mixer */ >+ { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x2}, >+ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2}, >+ { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Speaker */ >+ /* unmute dac0 input in audio mixer */ >+ { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0x701f}, >+ /* unmute right and left channels for nodes 0x0a, 0xd, 0x0f */ >+ { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {} >+}; >+ >+static struct hda_verb stac925x_core_init[] = { >+ /* set dac0mux for dac converter */ >+ { 0x06, AC_VERB_SET_CONNECT_SEL, 0x00}, >+ {} >+}; >+ >+static struct hda_verb stac922x_core_init[] = { >+ /* set master volume and direct control */ >+ { 0x16, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, >+ {} >+}; >+ >+static 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 struct hda_verb stac927x_core_init[] = { >+ /* set master volume and direct control */ >+ { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, >+ {} >+}; >+ >+static struct hda_verb stac9205_core_init[] = { >+ /* set master volume and direct control */ >+ { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, >+ {} >+}; >+ >+#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_INPUT_SOURCE(cnt) \ >+ { \ >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ >+ .name = "Input Source", \ >+ .count = cnt, \ >+ .info = stac92xx_mux_enum_info, \ >+ .get = stac92xx_mux_enum_get, \ >+ .put = stac92xx_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), \ >+ } >+ >+static struct snd_kcontrol_new stac9200_mixer[] = { >+ HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT), >+ STAC_INPUT_SOURCE(1), >+ HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Capture Mux Volume", 0x0c, 0, HDA_OUTPUT), >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new stac92hd73xx_6ch_mixer[] = { >+ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3), >+ >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT), >+ >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT), >+ >+ HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT), >+ HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT), >+ >+ HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT), >+ >+ HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT), >+ HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT), >+ >+ HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT), >+ HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT), >+ >+ HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT), >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new stac92hd73xx_8ch_mixer[] = { >+ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 4), >+ >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT), >+ >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT), >+ >+ HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT), >+ HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT), >+ >+ HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT), >+ >+ HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT), >+ HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT), >+ >+ HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT), >+ HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT), >+ >+ HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT), >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new stac92hd73xx_10ch_mixer[] = { >+ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 5), >+ >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT), >+ >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT), >+ >+ HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT), >+ HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT), >+ >+ HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT), >+ HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT), >+ >+ HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT), >+ HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT), >+ >+ HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT), >+ HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT), >+ >+ HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT), >+ HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT), >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new stac92hd71bxx_analog_mixer[] = { >+ STAC_INPUT_SOURCE(2), >+ >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x0, 0x1a, 0x0, HDA_OUTPUT), >+ >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1d, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x0, HDA_OUTPUT), >+ >+ HDA_CODEC_MUTE("Analog Loopback 1", 0x17, 0x3, HDA_INPUT), >+ HDA_CODEC_MUTE("Analog Loopback 2", 0x17, 0x4, HDA_INPUT), >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new stac92hd71bxx_mixer[] = { >+ STAC_INPUT_SOURCE(2), >+ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2), >+ >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x0, 0x1a, 0x0, HDA_OUTPUT), >+ >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1d, 0x0, HDA_OUTPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x0, HDA_OUTPUT), >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new stac925x_mixer[] = { >+ STAC_INPUT_SOURCE(1), >+ HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_OUTPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x14, 0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Capture Mux Volume", 0x0f, 0, HDA_OUTPUT), >+ { } /* end */ >+}; >+ >+static struct snd_kcontrol_new stac9205_mixer[] = { >+ STAC_INPUT_SOURCE(2), >+ STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0, 1), >+ >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1b, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1d, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x0, 0x19, 0x0, HDA_OUTPUT), >+ >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1c, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1e, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x1, 0x1A, 0x0, HDA_OUTPUT), >+ >+ { } /* end */ >+}; >+ >+/* This needs to be generated dynamically based on sequence */ >+static struct snd_kcontrol_new stac922x_mixer[] = { >+ STAC_INPUT_SOURCE(2), >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x17, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x17, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x0, 0x12, 0x0, HDA_OUTPUT), >+ >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x18, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x18, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x1, 0x13, 0x0, HDA_OUTPUT), >+ { } /* end */ >+}; >+ >+ >+static struct snd_kcontrol_new stac927x_mixer[] = { >+ STAC_INPUT_SOURCE(3), >+ STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1), >+ >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x18, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1b, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x0, 0x15, 0x0, HDA_OUTPUT), >+ >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x19, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1c, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x1, 0x16, 0x0, HDA_OUTPUT), >+ >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 0x2, 0x1A, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 0x2, 0x1d, 0x0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x2, 0x17, 0x0, HDA_OUTPUT), >+ { } /* end */ >+}; >+ >+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 const char *slave_vols[] = { >+ "Front Playback Volume", >+ "Surround Playback Volume", >+ "Center Playback Volume", >+ "LFE Playback Volume", >+ "Side Playback Volume", >+ "Headphone Playback Volume", >+ "Headphone Playback Volume", >+ "Speaker Playback Volume", >+ "External Speaker Playback Volume", >+ "Speaker2 Playback Volume", >+ NULL >+}; >+ >+static const char *slave_sws[] = { >+ "Front Playback Switch", >+ "Surround Playback Switch", >+ "Center Playback Switch", >+ "LFE Playback Switch", >+ "Side Playback Switch", >+ "Headphone Playback Switch", >+ "Headphone Playback Switch", >+ "Speaker Playback Switch", >+ "External Speaker Playback Switch", >+ "Speaker2 Playback Switch", >+ "IEC958 Playback Switch", >+ NULL >+}; >+ >+static int stac92xx_build_controls(struct hda_codec *codec) >+{ >+ struct sigmatel_spec *spec = codec->spec; >+ int err; >+ int i; >+ >+ 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->num_dmuxes > 0) { >+ stac_dmux_mixer.count = spec->num_dmuxes; >+ err = snd_ctl_add(codec->bus->card, >+ snd_ctl_new1(&stac_dmux_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); >+ 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, slave_vols); >+ 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, slave_sws); >+ if (err < 0) >+ return err; >+ } >+ >+ return 0; >+} >+ >+static unsigned int ref9200_pin_configs[8] = { >+ 0x01c47010, 0x01447010, 0x0221401f, 0x01114010, >+ 0x02a19020, 0x01a19021, 0x90100140, 0x01813122, >+}; >+ >+/* >+ STAC 9200 pin configs for >+ 102801A8 >+ 102801DE >+ 102801E8 >+*/ >+static unsigned int dell9200_d21_pin_configs[8] = { >+ 0x400001f0, 0x400001f1, 0x02214030, 0x01014010, >+ 0x02a19020, 0x01a19021, 0x90100140, 0x01813122, >+}; >+ >+/* >+ STAC 9200 pin configs for >+ 102801C0 >+ 102801C1 >+*/ >+static 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 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 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 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 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 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 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 unsigned int dell9200_m26_pin_configs[8] = { >+ 0x40c003fa, 0x404003fb, 0x0421121f, 0x90170310, >+ 0x408003fc, 0x04a11020, 0x401003fd, 0x403003fe, >+}; >+ >+/* >+ STAC 9200-32 >+ 102801CD (Dell Inspiron E1705/9400) >+*/ >+static unsigned int dell9200_m27_pin_configs[8] = { >+ 0x40c003fa, 0x01441340, 0x0421121f, 0x90170310, >+ 0x90170310, 0x04a11020, 0x90170310, 0x40f003fc, >+}; >+ >+static unsigned int oqo9200_pin_configs[8] = { >+ 0x40c000f0, 0x404000f1, 0x0221121f, 0x02211210, >+ 0x90170111, 0x90a70120, 0x400000f2, 0x400000f3, >+}; >+ >+ >+static unsigned int *stac9200_brd_tbl[STAC_9200_MODELS] = { >+ [STAC_REF] = ref9200_pin_configs, >+ [STAC_9200_OQO] = oqo9200_pin_configs, >+ [STAC_9200_DELL_D21] = dell9200_d21_pin_configs, >+ [STAC_9200_DELL_D22] = dell9200_d22_pin_configs, >+ [STAC_9200_DELL_D23] = dell9200_d23_pin_configs, >+ [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_PANASONIC] = ref9200_pin_configs, >+}; >+ >+static const char *stac9200_models[STAC_9200_MODELS] = { >+ [STAC_REF] = "ref", >+ [STAC_9200_OQO] = "oqo", >+ [STAC_9200_DELL_D21] = "dell-d21", >+ [STAC_9200_DELL_D22] = "dell-d22", >+ [STAC_9200_DELL_D23] = "dell-d23", >+ [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_GATEWAY] = "gateway", >+ [STAC_9200_PANASONIC] = "panasonic", >+}; >+ >+static struct snd_pci_quirk stac9200_cfg_tbl[] = { >+ /* SigmaTel reference board */ >+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, >+ "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_GATEWAY), >+ SND_PCI_QUIRK(0x107b, 0x0317, "Gateway MT3423, MX341*", >+ STAC_9200_GATEWAY), >+ SND_PCI_QUIRK(0x107b, 0x0318, "Gateway ML3019, MT3707", >+ STAC_9200_GATEWAY), >+ /* OQO Mobile */ >+ SND_PCI_QUIRK(0x1106, 0x3288, "OQO Model 2", STAC_9200_OQO), >+ {} /* terminator */ >+}; >+ >+static unsigned int ref925x_pin_configs[8] = { >+ 0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021, >+ 0x90a70320, 0x02214210, 0x01019020, 0x9033032e, >+}; >+ >+static unsigned int stac925x_MA6_pin_configs[8] = { >+ 0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021, >+ 0x90a70320, 0x90100211, 0x400003f1, 0x9033032e, >+}; >+ >+static unsigned int stac925x_PA6_pin_configs[8] = { >+ 0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021, >+ 0x50a103f0, 0x90100211, 0x400003f1, 0x9033032e, >+}; >+ >+static unsigned int stac925xM2_2_pin_configs[8] = { >+ 0x40c003f3, 0x424503f2, 0x04180011, 0x02a19020, >+ 0x50a103f0, 0x90100212, 0x400003f1, 0x9033032e, >+}; >+ >+static unsigned int *stac925x_brd_tbl[STAC_925x_MODELS] = { >+ [STAC_REF] = ref925x_pin_configs, >+ [STAC_M2_2] = stac925xM2_2_pin_configs, >+ [STAC_MA6] = stac925x_MA6_pin_configs, >+ [STAC_PA6] = stac925x_PA6_pin_configs, >+}; >+ >+static const char *stac925x_models[STAC_925x_MODELS] = { >+ [STAC_REF] = "ref", >+ [STAC_M2_2] = "m2-2", >+ [STAC_MA6] = "m6", >+ [STAC_PA6] = "pa6", >+}; >+ >+static 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(0x8384, 0x7632, "Stac9202 Reference Board", STAC_REF), >+ SND_PCI_QUIRK(0x107b, 0x0316, "Gateway M255", STAC_REF), >+ SND_PCI_QUIRK(0x107b, 0x0366, "Gateway MP6954", STAC_REF), >+ SND_PCI_QUIRK(0x107b, 0x0461, "Gateway NX560XL", STAC_MA6), >+ SND_PCI_QUIRK(0x107b, 0x0681, "Gateway NX860", STAC_PA6), >+ SND_PCI_QUIRK(0x1002, 0x437b, "Gateway MX6453", STAC_M2_2), >+ {} /* terminator */ >+}; >+ >+static unsigned int ref92hd73xx_pin_configs[13] = { >+ 0x02214030, 0x02a19040, 0x01a19020, 0x02214030, >+ 0x0181302e, 0x01014010, 0x01014020, 0x01014030, >+ 0x02319040, 0x90a000f0, 0x90a000f0, 0x01452050, >+ 0x01452050, >+}; >+ >+static unsigned int dell_m6_pin_configs[13] = { >+ 0x0321101f, 0x4f00000f, 0x4f0000f0, 0x90170110, >+ 0x03a11020, 0x0321101f, 0x4f0000f0, 0x4f0000f0, >+ 0x4f0000f0, 0x90a60160, 0x4f0000f0, 0x4f0000f0, >+ 0x4f0000f0, >+}; >+ >+static unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = { >+ [STAC_92HD73XX_REF] = ref92hd73xx_pin_configs, >+ [STAC_DELL_M6] = dell_m6_pin_configs, >+}; >+ >+static const char *stac92hd73xx_models[STAC_92HD73XX_MODELS] = { >+ [STAC_92HD73XX_REF] = "ref", >+ [STAC_DELL_M6] = "dell-m6", >+}; >+ >+static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = { >+ /* SigmaTel reference board */ >+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, >+ "DFI LanParty", STAC_92HD73XX_REF), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0254, >+ "unknown Dell", STAC_DELL_M6), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0255, >+ "unknown Dell", STAC_DELL_M6), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0256, >+ "unknown Dell", STAC_DELL_M6), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0257, >+ "unknown Dell", STAC_DELL_M6), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x025e, >+ "unknown Dell", STAC_DELL_M6), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x025f, >+ "unknown Dell", STAC_DELL_M6), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0271, >+ "unknown Dell", STAC_DELL_M6), >+ {} /* terminator */ >+}; >+ >+static unsigned int ref92hd71bxx_pin_configs[10] = { >+ 0x02214030, 0x02a19040, 0x01a19020, 0x01014010, >+ 0x0181302e, 0x01114010, 0x01019020, 0x90a000f0, >+ 0x90a000f0, 0x01452050, >+}; >+ >+static unsigned int dell_m4_1_pin_configs[13] = { >+ 0x0421101f, 0x04a11221, 0x40f000f0, 0x90170110, >+ 0x23a1902e, 0x23014250, 0x40f000f0, 0x90a000f0, >+ 0x40f000f0, 0x4f0000f0, >+}; >+ >+static unsigned int dell_m4_2_pin_configs[13] = { >+ 0x0421101f, 0x04a11221, 0x90a70330, 0x90170110, >+ 0x23a1902e, 0x23014250, 0x40f000f0, 0x40f000f0, >+ 0x40f000f0, 0x044413b0, >+}; >+ >+static unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = { >+ [STAC_92HD71BXX_REF] = ref92hd71bxx_pin_configs, >+ [STAC_DELL_M4_1] = dell_m4_1_pin_configs, >+ [STAC_DELL_M4_2] = dell_m4_2_pin_configs, >+}; >+ >+static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = { >+ [STAC_92HD71BXX_REF] = "ref", >+ [STAC_DELL_M4_1] = "dell-m4-1", >+ [STAC_DELL_M4_2] = "dell-m4-2", >+}; >+ >+static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = { >+ /* SigmaTel reference board */ >+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, >+ "DFI LanParty", STAC_92HD71BXX_REF), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233, >+ "unknown Dell", STAC_DELL_M4_1), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0234, >+ "unknown Dell", STAC_DELL_M4_1), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0250, >+ "unknown Dell", STAC_DELL_M4_1), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x024f, >+ "unknown Dell", STAC_DELL_M4_1), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x024d, >+ "unknown Dell", STAC_DELL_M4_1), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0251, >+ "unknown Dell", STAC_DELL_M4_1), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0277, >+ "unknown Dell", STAC_DELL_M4_1), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0263, >+ "unknown Dell", STAC_DELL_M4_2), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0265, >+ "unknown Dell", STAC_DELL_M4_2), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0262, >+ "unknown Dell", STAC_DELL_M4_2), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0264, >+ "unknown Dell", STAC_DELL_M4_2), >+ {} /* terminator */ >+}; >+ >+static 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 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 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 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 unsigned int dell_922x_m82_pin_configs[10] = { >+ 0x02211211, 0x408103ff, 0x02a1123e, 0x90100310, >+ 0x408003f1, 0x0221121f, 0x03451340, 0x40c003f2, >+ 0x508003f3, 0x405003f4, >+}; >+ >+static unsigned int d945gtp3_pin_configs[10] = { >+ 0x0221401f, 0x01a19022, 0x01813021, 0x01014010, >+ 0x40000100, 0x40000100, 0x40000100, 0x40000100, >+ 0x02a19120, 0x40000100, >+}; >+ >+static unsigned int d945gtp5_pin_configs[10] = { >+ 0x0221401f, 0x01011012, 0x01813024, 0x01014010, >+ 0x01a19021, 0x01016011, 0x01452130, 0x40000100, >+ 0x02a19320, 0x40000100, >+}; >+ >+static unsigned int intel_mac_v1_pin_configs[10] = { >+ 0x0121e21f, 0x400000ff, 0x9017e110, 0x400000fd, >+ 0x400000fe, 0x0181e020, 0x1145e030, 0x11c5e240, >+ 0x400000fc, 0x400000fb, >+}; >+ >+static unsigned int intel_mac_v2_pin_configs[10] = { >+ 0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd, >+ 0x400000fe, 0x0181e020, 0x1145e230, 0x500000fa, >+ 0x400000fc, 0x400000fb, >+}; >+ >+static unsigned int intel_mac_v3_pin_configs[10] = { >+ 0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd, >+ 0x400000fe, 0x0181e020, 0x1145e230, 0x11c5e240, >+ 0x400000fc, 0x400000fb, >+}; >+ >+static unsigned int intel_mac_v4_pin_configs[10] = { >+ 0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f, >+ 0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240, >+ 0x400000fc, 0x400000fb, >+}; >+ >+static unsigned int intel_mac_v5_pin_configs[10] = { >+ 0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f, >+ 0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240, >+ 0x400000fc, 0x400000fb, >+}; >+ >+ >+static 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, >+ /* 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_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 *stac922x_models[STAC_922X_MODELS] = { >+ [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", >+ /* 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_922X_DELL_D81] = "dell-d81", >+ [STAC_922X_DELL_D82] = "dell-d82", >+ [STAC_922X_DELL_M81] = "dell-m81", >+ [STAC_922X_DELL_M82] = "dell-m82", >+}; >+ >+static struct snd_pci_quirk stac922x_cfg_tbl[] = { >+ /* SigmaTel reference board */ >+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, >+ "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 systems */ >+ /* Apple Mac Mini (early 2006) */ >+ SND_PCI_QUIRK(0x8384, 0x7680, >+ "Mac Mini", STAC_INTEL_MAC_V3), >+ /* 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), >+ {} /* terminator */ >+}; >+ >+static unsigned int ref927x_pin_configs[14] = { >+ 0x02214020, 0x02a19080, 0x0181304e, 0x01014010, >+ 0x01a19040, 0x01011012, 0x01016011, 0x0101201f, >+ 0x183301f0, 0x18a001f0, 0x18a001f0, 0x01442070, >+ 0x01c42190, 0x40000100, >+}; >+ >+static unsigned int d965_3st_pin_configs[14] = { >+ 0x0221401f, 0x02a19120, 0x40000100, 0x01014011, >+ 0x01a19021, 0x01813024, 0x40000100, 0x40000100, >+ 0x40000100, 0x40000100, 0x40000100, 0x40000100, >+ 0x40000100, 0x40000100 >+}; >+ >+static unsigned int d965_5st_pin_configs[14] = { >+ 0x02214020, 0x02a19080, 0x0181304e, 0x01014010, >+ 0x01a19040, 0x01011012, 0x01016011, 0x40000100, >+ 0x40000100, 0x40000100, 0x40000100, 0x01442070, >+ 0x40000100, 0x40000100 >+}; >+ >+static unsigned int dell_3st_pin_configs[14] = { >+ 0x02211230, 0x02a11220, 0x01a19040, 0x01114210, >+ 0x01111212, 0x01116211, 0x01813050, 0x01112214, >+ 0x403003fa, 0x90a60040, 0x90a60040, 0x404003fb, >+ 0x40c003fc, 0x40000100 >+}; >+ >+static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = { >+ [STAC_D965_REF] = ref927x_pin_configs, >+ [STAC_D965_3ST] = d965_3st_pin_configs, >+ [STAC_D965_5ST] = d965_5st_pin_configs, >+ [STAC_DELL_3ST] = dell_3st_pin_configs, >+ [STAC_DELL_BIOS] = NULL, >+}; >+ >+static const char *stac927x_models[STAC_927X_MODELS] = { >+ [STAC_D965_REF] = "ref", >+ [STAC_D965_3ST] = "3stack", >+ [STAC_D965_5ST] = "5stack", >+ [STAC_DELL_3ST] = "dell-3stack", >+ [STAC_DELL_BIOS] = "dell-bios", >+}; >+ >+static struct snd_pci_quirk stac927x_cfg_tbl[] = { >+ /* SigmaTel reference board */ >+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, >+ "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(PCI_VENDOR_ID_INTEL, 0x2116, "Intel D965", STAC_D965_3ST), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2115, "Intel D965", STAC_D965_3ST), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2114, "Intel D965", STAC_D965_3ST), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2113, "Intel D965", STAC_D965_3ST), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2112, "Intel D965", STAC_D965_3ST), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2111, "Intel D965", STAC_D965_3ST), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2110, "Intel D965", STAC_D965_3ST), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2009, "Intel D965", STAC_D965_3ST), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2008, "Intel D965", STAC_D965_3ST), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2007, "Intel D965", STAC_D965_3ST), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2006, "Intel D965", STAC_D965_3ST), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2005, "Intel D965", STAC_D965_3ST), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2004, "Intel D965", STAC_D965_3ST), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2003, "Intel D965", STAC_D965_3ST), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2002, "Intel D965", STAC_D965_3ST), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2001, "Intel D965", STAC_D965_3ST), >+ /* Dell 3 stack systems */ >+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f7, "Dell XPS M1730", STAC_DELL_3ST), >+ 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, 0x0227, "Dell Vostro 1400 ", STAC_DELL_BIOS), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022f, "Dell ", STAC_DELL_BIOS), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022e, "Dell ", 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(PCI_VENDOR_ID_INTEL, 0x2301, "Intel D965", STAC_D965_5ST), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2302, "Intel D965", STAC_D965_5ST), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2303, "Intel D965", STAC_D965_5ST), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2304, "Intel D965", STAC_D965_5ST), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2305, "Intel D965", STAC_D965_5ST), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2501, "Intel D965", STAC_D965_5ST), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2502, "Intel D965", STAC_D965_5ST), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2503, "Intel D965", STAC_D965_5ST), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2504, "Intel D965", STAC_D965_5ST), >+ {} /* terminator */ >+}; >+ >+static 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) >+*/ >+static 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 unsigned int dell_9205_m43_pin_configs[12] = { >+ 0x0321101f, 0x03a11020, 0x90a70330, 0x90170310, >+ 0x400000fe, 0x400000ff, 0x400000fd, 0x40f000f9, >+ 0x400000fa, 0x400000fc, 0x0144131f, 0x40c003f8, >+}; >+ >+static unsigned int dell_9205_m44_pin_configs[12] = { >+ 0x0421101f, 0x04a11020, 0x400003fa, 0x90170310, >+ 0x400003fb, 0x400003fc, 0x400003fd, 0x400003f9, >+ 0x90a60330, 0x400003ff, 0x01441340, 0x40c003fe, >+}; >+ >+static 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, >+}; >+ >+static const char *stac9205_models[STAC_9205_MODELS] = { >+ [STAC_9205_REF] = "ref", >+ [STAC_9205_DELL_M42] = "dell-m42", >+ [STAC_9205_DELL_M43] = "dell-m43", >+ [STAC_9205_DELL_M44] = "dell-m44", >+}; >+ >+static 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_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, 0x021c, >+ "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, 0x021b, >+ "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, 0x0206, >+ "Dell Precision", STAC_9205_DELL_M43), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f1, >+ "Dell Inspiron", STAC_9205_DELL_M44), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f2, >+ "Dell Inspiron", STAC_9205_DELL_M44), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fc, >+ "Dell Inspiron", STAC_9205_DELL_M44), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fd, >+ "Dell Inspiron", STAC_9205_DELL_M44), >+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0204, >+ "unknown Dell", STAC_9205_DELL_M42), >+ 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), >+ {} /* terminator */ >+}; >+ >+static int stac92xx_save_bios_config_regs(struct hda_codec *codec) >+{ >+ int i; >+ struct sigmatel_spec *spec = codec->spec; >+ >+ if (! spec->bios_pin_configs) { >+ spec->bios_pin_configs = kcalloc(spec->num_pins, >+ sizeof(*spec->bios_pin_configs), GFP_KERNEL); >+ if (! spec->bios_pin_configs) >+ return -ENOMEM; >+ } >+ >+ for (i = 0; i < spec->num_pins; i++) { >+ hda_nid_t nid = spec->pin_nids[i]; >+ unsigned int pin_cfg; >+ >+ pin_cfg = snd_hda_codec_read(codec, nid, 0, >+ AC_VERB_GET_CONFIG_DEFAULT, 0x00); >+ snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x bios pin config %8.8x\n", >+ nid, pin_cfg); >+ spec->bios_pin_configs[i] = pin_cfg; >+ } >+ >+ return 0; >+} >+ >+static void stac92xx_set_config_reg(struct hda_codec *codec, >+ hda_nid_t pin_nid, unsigned int pin_config) >+{ >+ int i; >+ snd_hda_codec_write(codec, pin_nid, 0, >+ AC_VERB_SET_CONFIG_DEFAULT_BYTES_0, >+ pin_config & 0x000000ff); >+ snd_hda_codec_write(codec, pin_nid, 0, >+ AC_VERB_SET_CONFIG_DEFAULT_BYTES_1, >+ (pin_config & 0x0000ff00) >> 8); >+ snd_hda_codec_write(codec, pin_nid, 0, >+ AC_VERB_SET_CONFIG_DEFAULT_BYTES_2, >+ (pin_config & 0x00ff0000) >> 16); >+ snd_hda_codec_write(codec, pin_nid, 0, >+ AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, >+ pin_config >> 24); >+ i = snd_hda_codec_read(codec, pin_nid, 0, >+ AC_VERB_GET_CONFIG_DEFAULT, >+ 0x00); >+ snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x pin config %8.8x\n", >+ pin_nid, i); >+} >+ >+static void stac92xx_set_config_regs(struct hda_codec *codec) >+{ >+ int i; >+ struct sigmatel_spec *spec = codec->spec; >+ >+ if (!spec->pin_configs) >+ return; >+ >+ for (i = 0; i < spec->num_pins; i++) >+ stac92xx_set_config_reg(codec, spec->pin_nids[i], >+ spec->pin_configs[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; >+ 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); >+} >+ >+ >+/* >+ * 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; >+ >+ snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], >+ 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; >+ >+ snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); >+ return 0; >+} >+ >+static 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 >+ }, >+}; >+ >+static struct hda_pcm_stream stac92xx_pcm_digital_capture = { >+ .substreams = 1, >+ .channels_min = 2, >+ .channels_max = 2, >+ /* NID is set in stac92xx_build_pcms */ >+}; >+ >+static 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 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 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_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 = HDA_PCM_TYPE_SPDIF; >+ if (spec->multiout.dig_out_nid) { >+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_digital_playback; >+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; >+ } >+ 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_vref(struct hda_codec *codec, hda_nid_t nid) >+{ >+ unsigned int pincap = snd_hda_param_read(codec, nid, >+ AC_PAR_PIN_CAP); >+ 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 int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol, >+ struct snd_ctl_elem_value *ucontrol) >+{ >+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); >+ struct sigmatel_spec *spec = codec->spec; >+ >+ spec->hp_switch = ucontrol->value.integer.value[0]; >+ >+ /* check to be sure that the ports are upto date with >+ * switch changes >+ */ >+ codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26); >+ >+ return 1; >+} >+ >+#define stac92xx_io_switch_info snd_ctl_boolean_mono_info >+ >+static int stac92xx_io_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) >+{ >+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); >+ struct sigmatel_spec *spec = codec->spec; >+ int io_idx = kcontrol-> private_value & 0xff; >+ >+ ucontrol->value.integer.value[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 >> 8; >+ int io_idx = kcontrol-> private_value & 0xff; >+ unsigned short val = !!ucontrol->value.integer.value[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_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) >+ codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26); >+ >+ 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_MONO_MUX, >+ STAC_CTL_WIDGET_HP_SWITCH, >+ STAC_CTL_WIDGET_IO_SWITCH, >+ STAC_CTL_WIDGET_CLFE_SWITCH >+}; >+ >+static struct snd_kcontrol_new stac92xx_control_templates[] = { >+ HDA_CODEC_VOLUME(NULL, 0, 0, 0), >+ HDA_CODEC_MUTE(NULL, 0, 0, 0), >+ STAC_MONO_MUX, >+ STAC_CODEC_HP_SWITCH(NULL), >+ STAC_CODEC_IO_SWITCH(NULL, 0), >+ STAC_CODEC_CLFE_SWITCH(NULL, 0), >+}; >+ >+/* add dynamic controls */ >+static int stac92xx_add_control(struct sigmatel_spec *spec, int type, const char *name, unsigned long val) >+{ >+ struct snd_kcontrol_new *knew; >+ >+ if (spec->num_kctl_used >= spec->num_kctl_alloc) { >+ int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC; >+ >+ knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */ >+ if (! knew) >+ return -ENOMEM; >+ if (spec->kctl_alloc) { >+ memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc); >+ kfree(spec->kctl_alloc); >+ } >+ spec->kctl_alloc = knew; >+ spec->num_kctl_alloc = num; >+ } >+ >+ knew = &spec->kctl_alloc[spec->num_kctl_used]; >+ *knew = stac92xx_control_templates[type]; >+ knew->name = kstrdup(name, GFP_KERNEL); >+ if (! knew->name) >+ return -ENOMEM; >+ knew->private_value = val; >+ spec->num_kctl_used++; >+ return 0; >+} >+ >+/* flag inputs as additional dynamic lineouts */ >+static int stac92xx_add_dyn_out_pins(struct hda_codec *codec, struct auto_pin_cfg *cfg) >+{ >+ struct sigmatel_spec *spec = codec->spec; >+ unsigned int wcaps, wtype; >+ int i, num_dacs = 0; >+ >+ /* use the wcaps cache to count all DACs available for line-outs */ >+ for (i = 0; i < codec->num_nodes; i++) { >+ wcaps = codec->wcaps[i]; >+ wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; >+ >+ if (wtype == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL)) >+ num_dacs++; >+ } >+ >+ snd_printdd("%s: total dac count=%d\n", __func__, num_dacs); >+ >+ switch (cfg->line_outs) { >+ case 3: >+ /* add line-in as side */ >+ if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 3) { >+ cfg->line_out_pins[cfg->line_outs] = >+ cfg->input_pins[AUTO_PIN_LINE]; >+ spec->line_switch = 1; >+ cfg->line_outs++; >+ } >+ break; >+ case 2: >+ /* add line-in as clfe and mic as side */ >+ if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 2) { >+ cfg->line_out_pins[cfg->line_outs] = >+ cfg->input_pins[AUTO_PIN_LINE]; >+ spec->line_switch = 1; >+ cfg->line_outs++; >+ } >+ if (cfg->input_pins[AUTO_PIN_MIC] && num_dacs > 3) { >+ cfg->line_out_pins[cfg->line_outs] = >+ cfg->input_pins[AUTO_PIN_MIC]; >+ spec->mic_switch = 1; >+ cfg->line_outs++; >+ } >+ break; >+ case 1: >+ /* add line-in as surr and mic as clfe */ >+ if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 1) { >+ cfg->line_out_pins[cfg->line_outs] = >+ cfg->input_pins[AUTO_PIN_LINE]; >+ spec->line_switch = 1; >+ cfg->line_outs++; >+ } >+ if (cfg->input_pins[AUTO_PIN_MIC] && num_dacs > 2) { >+ cfg->line_out_pins[cfg->line_outs] = >+ cfg->input_pins[AUTO_PIN_MIC]; >+ spec->mic_switch = 1; >+ cfg->line_outs++; >+ } >+ break; >+ } >+ >+ 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; >+} >+ >+/* >+ * 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 auto_pin_cfg *cfg) >+{ >+ struct sigmatel_spec *spec = codec->spec; >+ int i, j, conn_len = 0; >+ hda_nid_t nid, conn[HDA_MAX_CONNECTIONS]; >+ unsigned int wcaps, wtype; >+ >+ for (i = 0; i < cfg->line_outs; i++) { >+ nid = cfg->line_out_pins[i]; >+ conn_len = snd_hda_get_connections(codec, nid, conn, >+ HDA_MAX_CONNECTIONS); >+ for (j = 0; j < conn_len; j++) { >+ wcaps = snd_hda_param_read(codec, conn[j], >+ AC_PAR_AUDIO_WIDGET_CAP); >+ wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; >+ if (wtype != AC_WID_AUD_OUT || >+ (wcaps & AC_WCAP_DIGITAL)) >+ continue; >+ /* conn[j] is a DAC routed to this line-out */ >+ if (!is_in_dac_nids(spec, conn[j])) >+ break; >+ } >+ >+ if (j == conn_len) { >+ 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; >+ } >+ >+ spec->multiout.dac_nids[i] = conn[j]; >+ spec->multiout.num_dacs++; >+ if (conn_len > 1) { >+ /* select this DAC in the pin's input mux */ >+ snd_hda_codec_write_cache(codec, nid, 0, >+ AC_VERB_SET_CONNECT_SEL, j); >+ >+ } >+ } >+ >+ snd_printd("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(struct sigmatel_spec *spec, const char *pfx, hda_nid_t nid, int chs) >+{ >+ char name[32]; >+ int err; >+ >+ sprintf(name, "%s Playback Volume", pfx); >+ err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, name, >+ HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ sprintf(name, "%s Playback Switch", pfx); >+ err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, name, >+ HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ return 0; >+} >+ >+static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid) >+{ >+ if (!spec->multiout.hp_nid) >+ spec->multiout.hp_nid = nid; >+ else if (spec->multiout.num_dacs > 4) { >+ printk(KERN_WARNING "stac92xx: No space for DAC 0x%x\n", nid); >+ return 1; >+ } else { >+ spec->multiout.dac_nids[spec->multiout.num_dacs] = nid; >+ spec->multiout.num_dacs++; >+ } >+ return 0; >+} >+ >+static int check_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid) >+{ >+ if (is_in_dac_nids(spec, nid)) >+ return 1; >+ if (spec->multiout.hp_nid == nid) >+ return 1; >+ return 0; >+} >+ >+/* add playback controls from the parsed DAC table */ >+static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, >+ const struct auto_pin_cfg *cfg) >+{ >+ static const char *chname[4] = { >+ "Front", "Surround", NULL /*CLFE*/, "Side" >+ }; >+ hda_nid_t nid; >+ int i, err; >+ >+ struct sigmatel_spec *spec = codec->spec; >+ unsigned int wid_caps, pincap; >+ >+ >+ for (i = 0; i < cfg->line_outs && i < spec->multiout.num_dacs; i++) { >+ if (!spec->multiout.dac_nids[i]) >+ continue; >+ >+ nid = spec->multiout.dac_nids[i]; >+ >+ if (i == 2) { >+ /* Center/LFE */ >+ err = create_controls(spec, "Center", nid, 1); >+ if (err < 0) >+ return err; >+ err = create_controls(spec, "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 { >+ err = create_controls(spec, chname[i], nid, 3); >+ if (err < 0) >+ return err; >+ } >+ } >+ >+ if (cfg->hp_outs > 1) { >+ err = stac92xx_add_control(spec, >+ STAC_CTL_WIDGET_HP_SWITCH, >+ "Headphone as Line Out Switch", 0); >+ if (err < 0) >+ return err; >+ } >+ >+ if (spec->line_switch) { >+ nid = cfg->input_pins[AUTO_PIN_LINE]; >+ pincap = snd_hda_param_read(codec, nid, >+ AC_PAR_PIN_CAP); >+ if (pincap & AC_PINCAP_OUT) { >+ err = stac92xx_add_control(spec, >+ STAC_CTL_WIDGET_IO_SWITCH, >+ "Line In as Output Switch", nid << 8); >+ if (err < 0) >+ return err; >+ } >+ } >+ >+ if (spec->mic_switch) { >+ unsigned int def_conf; >+ unsigned int mic_pin = AUTO_PIN_MIC; >+again: >+ nid = cfg->input_pins[mic_pin]; >+ def_conf = snd_hda_codec_read(codec, nid, 0, >+ AC_VERB_GET_CONFIG_DEFAULT, 0); >+ /* some laptops have an internal analog microphone >+ * which can't be used as a output */ >+ if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) { >+ pincap = snd_hda_param_read(codec, nid, >+ AC_PAR_PIN_CAP); >+ if (pincap & AC_PINCAP_OUT) { >+ err = stac92xx_add_control(spec, >+ STAC_CTL_WIDGET_IO_SWITCH, >+ "Mic as Output Switch", (nid << 8) | 1); >+ nid = snd_hda_codec_read(codec, nid, 0, >+ AC_VERB_GET_CONNECT_LIST, 0) & 0xff; >+ if (!check_in_dac_nids(spec, nid)) >+ add_spec_dacs(spec, nid); >+ if (err < 0) >+ return err; >+ } >+ } else if (mic_pin == AUTO_PIN_MIC) { >+ mic_pin = AUTO_PIN_FRONT_MIC; >+ goto again; >+ } >+ } >+ >+ 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; >+ hda_nid_t nid; >+ int i, old_num_dacs, err; >+ >+ old_num_dacs = spec->multiout.num_dacs; >+ for (i = 0; i < cfg->hp_outs; i++) { >+ unsigned int wid_caps = get_wcaps(codec, cfg->hp_pins[i]); >+ if (wid_caps & AC_WCAP_UNSOL_CAP) >+ spec->hp_detect = 1; >+ nid = snd_hda_codec_read(codec, cfg->hp_pins[i], 0, >+ AC_VERB_GET_CONNECT_LIST, 0) & 0xff; >+ if (check_in_dac_nids(spec, nid)) >+ nid = 0; >+ if (! nid) >+ continue; >+ add_spec_dacs(spec, nid); >+ } >+ for (i = 0; i < cfg->speaker_outs; i++) { >+ nid = snd_hda_codec_read(codec, cfg->speaker_pins[i], 0, >+ AC_VERB_GET_CONNECT_LIST, 0) & 0xff; >+ if (check_in_dac_nids(spec, nid)) >+ nid = 0; >+ if (! nid) >+ continue; >+ add_spec_dacs(spec, nid); >+ } >+ for (i = 0; i < cfg->line_outs; i++) { >+ nid = snd_hda_codec_read(codec, cfg->line_out_pins[i], 0, >+ AC_VERB_GET_CONNECT_LIST, 0) & 0xff; >+ if (check_in_dac_nids(spec, nid)) >+ nid = 0; >+ if (! nid) >+ continue; >+ add_spec_dacs(spec, nid); >+ } >+ for (i = old_num_dacs; i < spec->multiout.num_dacs; i++) { >+ static const char *pfxs[] = { >+ "Speaker", "External Speaker", "Speaker2", >+ }; >+ err = create_controls(spec, pfxs[i - old_num_dacs], >+ spec->multiout.dac_nids[i], 3); >+ if (err < 0) >+ return err; >+ } >+ if (spec->multiout.hp_nid) { >+ err = create_controls(spec, "Headphone", >+ spec->multiout.hp_nid, 3); >+ if (err < 0) >+ return err; >+ } >+ >+ return 0; >+} >+ >+/* labels for mono mux outputs */ >+static const char *stac92xx_mono_labels[3] = { >+ "DAC0", "DAC1", "Mixer" >+}; >+ >+/* 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 || num_cons > ARRAY_SIZE(stac92xx_mono_labels)) >+ return -EINVAL; >+ >+ for (i = 0; i < num_cons; i++) { >+ mono_mux->items[mono_mux->num_items].label = >+ stac92xx_mono_labels[i]; >+ mono_mux->items[mono_mux->num_items].index = i; >+ mono_mux->num_items++; >+ } >+ >+ return stac92xx_add_control(spec, STAC_CTL_WIDGET_MONO_MUX, >+ "Mono Mux", spec->mono_nid); >+} >+ >+/* labels for dmic mux inputs */ >+static const char *stac92xx_dmic_labels[5] = { >+ "Analog Inputs", "Digital Mic 1", "Digital Mic 2", >+ "Digital Mic 3", "Digital Mic 4" >+}; >+ >+/* 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 *dimux = &spec->private_dimux; >+ hda_nid_t con_lst[HDA_MAX_NUM_INPUTS]; >+ int err, i, j; >+ char name[32]; >+ >+ dimux->items[dimux->num_items].label = stac92xx_dmic_labels[0]; >+ dimux->items[dimux->num_items].index = 0; >+ dimux->num_items++; >+ >+ for (i = 0; i < spec->num_dmics; i++) { >+ hda_nid_t nid; >+ int index; >+ int num_cons; >+ unsigned int wcaps; >+ unsigned int def_conf; >+ >+ def_conf = snd_hda_codec_read(codec, >+ spec->dmic_nids[i], >+ 0, >+ AC_VERB_GET_CONFIG_DEFAULT, >+ 0); >+ if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) >+ continue; >+ >+ nid = spec->dmic_nids[i]; >+ num_cons = snd_hda_get_connections(codec, >+ spec->dmux_nids[0], >+ con_lst, >+ HDA_MAX_NUM_INPUTS); >+ for (j = 0; j < num_cons; j++) >+ if (con_lst[j] == nid) { >+ index = j; >+ goto found; >+ } >+ continue; >+found: >+ wcaps = get_wcaps(codec, nid); >+ >+ if (wcaps & AC_WCAP_OUT_AMP) { >+ sprintf(name, "%s Capture Volume", >+ stac92xx_dmic_labels[dimux->num_items]); >+ >+ err = stac92xx_add_control(spec, >+ STAC_CTL_WIDGET_VOL, >+ name, >+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ } >+ >+ dimux->items[dimux->num_items].label = >+ stac92xx_dmic_labels[dimux->num_items]; >+ dimux->items[dimux->num_items].index = index; >+ dimux->num_items++; >+ } >+ >+ return 0; >+} >+ >+/* 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; >+ hda_nid_t con_lst[HDA_MAX_NUM_INPUTS]; >+ int i, j, k; >+ >+ for (i = 0; i < AUTO_PIN_LAST; i++) { >+ int index; >+ >+ if (!cfg->input_pins[i]) >+ continue; >+ index = -1; >+ for (j = 0; j < spec->num_muxes; j++) { >+ int num_cons; >+ num_cons = snd_hda_get_connections(codec, >+ spec->mux_nids[j], >+ con_lst, >+ HDA_MAX_NUM_INPUTS); >+ for (k = 0; k < num_cons; k++) >+ if (con_lst[k] == cfg->input_pins[i]) { >+ index = k; >+ goto found; >+ } >+ } >+ continue; >+ found: >+ imux->items[imux->num_items].label = auto_pin_cfg_labels[i]; >+ imux->items[imux->num_items].index = index; >+ 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 stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out, hda_nid_t dig_in) >+{ >+ struct sigmatel_spec *spec = codec->spec; >+ int err; >+ int hp_speaker_swap = 0; >+ >+ 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 (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT && >+ spec->autocfg.hp_outs > 1) { >+ /* Copy hp_outs to line_outs, backup line_outs in >+ * speaker_outs so that the following routines can handle >+ * HP pins as primary outputs. >+ */ >+ memcpy(spec->autocfg.speaker_pins, spec->autocfg.line_out_pins, >+ sizeof(spec->autocfg.line_out_pins)); >+ spec->autocfg.speaker_outs = spec->autocfg.line_outs; >+ memcpy(spec->autocfg.line_out_pins, spec->autocfg.hp_pins, >+ sizeof(spec->autocfg.hp_pins)); >+ spec->autocfg.line_outs = spec->autocfg.hp_outs; >+ hp_speaker_swap = 1; >+ } >+ if (spec->autocfg.mono_out_pin) { >+ int dir = (get_wcaps(codec, spec->autocfg.mono_out_pin) >+ & AC_WCAP_OUT_AMP) ? HDA_OUTPUT : HDA_INPUT; >+ 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)) { >+ >+ int wcaps = get_wcaps(codec, conn_list[0]); >+ int wid_type = (wcaps & AC_WCAP_TYPE) >+ >> AC_WCAP_TYPE_SHIFT; >+ /* 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]; >+ } >+ /* all mono outs have a least a mute/unmute switch */ >+ err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, >+ "Mono Playback Switch", >+ HDA_COMPOSE_AMP_VAL(spec->autocfg.mono_out_pin, >+ 1, 0, dir)); >+ 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, >+ "Mono Playback Volume", >+ HDA_COMPOSE_AMP_VAL(spec->autocfg.mono_out_pin, >+ 1, 0, dir)); >+ if (err < 0) >+ return err; >+ } >+ >+ stac92xx_auto_set_pinctl(codec, spec->autocfg.mono_out_pin, >+ AC_PINCTL_OUT_EN); >+ } >+ >+ if ((err = stac92xx_add_dyn_out_pins(codec, &spec->autocfg)) < 0) >+ return err; >+ if (spec->multiout.num_dacs == 0) >+ if ((err = stac92xx_auto_fill_dac_nids(codec, &spec->autocfg)) < 0) >+ return err; >+ >+ err = stac92xx_auto_create_multi_out_ctls(codec, &spec->autocfg); >+ >+ if (err < 0) >+ return err; >+ >+ if (hp_speaker_swap == 1) { >+ /* Restore the hp_outs and line_outs */ >+ memcpy(spec->autocfg.hp_pins, spec->autocfg.line_out_pins, >+ sizeof(spec->autocfg.line_out_pins)); >+ spec->autocfg.hp_outs = spec->autocfg.line_outs; >+ memcpy(spec->autocfg.line_out_pins, spec->autocfg.speaker_pins, >+ sizeof(spec->autocfg.speaker_pins)); >+ spec->autocfg.line_outs = spec->autocfg.speaker_outs; >+ memset(spec->autocfg.speaker_pins, 0, >+ sizeof(spec->autocfg.speaker_pins)); >+ spec->autocfg.speaker_outs = 0; >+ } >+ >+ err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg); >+ >+ if (err < 0) >+ 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) >+ if ((err = stac92xx_auto_create_dmic_input_ctls(codec, >+ &spec->autocfg)) < 0) >+ return err; >+ >+ spec->multiout.max_channels = spec->multiout.num_dacs * 2; >+ if (spec->multiout.max_channels > 2) >+ spec->surr_switch = 1; >+ >+ if (spec->autocfg.dig_out_pin) >+ spec->multiout.dig_out_nid = dig_out; >+ if (spec->autocfg.dig_in_pin) >+ spec->dig_in_nid = dig_in; >+ >+ if (spec->kctl_alloc) >+ spec->mixers[spec->num_mixers++] = spec->kctl_alloc; >+ >+ spec->input_mux = &spec->private_imux; >+ if (!spec->dinput_mux) >+ spec->dinput_mux = &spec->private_dimux; >+ 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]; >+ unsigned int wid_caps; >+ >+ if (! pin) >+ return 0; >+ >+ wid_caps = get_wcaps(codec, pin); >+ if (wid_caps & AC_WCAP_UNSOL_CAP) >+ 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_read(codec, pin, 0, >+ AC_VERB_GET_CONFIG_DEFAULT, >+ 0x00); >+ 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(spec, "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->autocfg.dig_out_pin) >+ spec->multiout.dig_out_nid = 0x05; >+ if (spec->autocfg.dig_in_pin) >+ spec->dig_in_nid = 0x04; >+ >+ if (spec->kctl_alloc) >+ spec->mixers[spec->num_mixers++] = spec->kctl_alloc; >+ >+ 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; >+ >+ 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 void enable_pin_detect(struct hda_codec *codec, hda_nid_t nid, >+ unsigned int event) >+{ >+ if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) >+ snd_hda_codec_write_cache(codec, nid, 0, >+ AC_VERB_SET_UNSOLICITED_ENABLE, >+ (AC_USRSP_EN | event)); >+} >+ >+static int is_nid_hp_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 */ >+ >+ return 0; /* nid is not a HP-Out */ >+}; >+ >+static void stac92xx_power_down(struct hda_codec *codec) >+{ >+ struct sigmatel_spec *spec = codec->spec; >+ >+ /* power down inactive DACs */ >+ hda_nid_t *dac; >+ for (dac = spec->dac_list; *dac; dac++) >+ if (!is_in_dac_nids(spec, *dac) && >+ spec->multiout.hp_nid != *dac) >+ snd_hda_codec_write_cache(codec, *dac, 0, >+ AC_VERB_SET_POWER_STATE, AC_PWRST_D3); >+} >+ >+static int stac92xx_init(struct hda_codec *codec) >+{ >+ struct sigmatel_spec *spec = codec->spec; >+ struct auto_pin_cfg *cfg = &spec->autocfg; >+ int i; >+ >+ snd_hda_sequence_write(codec, spec->init); >+ >+ /* set up pins */ >+ if (spec->hp_detect) { >+ /* Enable unsolicited responses on the HP widget */ >+ for (i = 0; i < cfg->hp_outs; i++) >+ enable_pin_detect(codec, cfg->hp_pins[i], >+ STAC_HP_EVENT); >+ /* force to enable the first line-out; the others are set up >+ * in unsol_event >+ */ >+ stac92xx_auto_set_pinctl(codec, spec->autocfg.line_out_pins[0], >+ AC_PINCTL_OUT_EN); >+ stac92xx_auto_init_hp_out(codec); >+ /* fake event to set up pins */ >+ codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26); >+ } else { >+ stac92xx_auto_init_multi_out(codec); >+ stac92xx_auto_init_hp_out(codec); >+ } >+ for (i = 0; i < AUTO_PIN_LAST; i++) { >+ hda_nid_t nid = cfg->input_pins[i]; >+ if (nid) { >+ unsigned int pinctl = AC_PINCTL_IN_EN; >+ if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC) >+ pinctl |= stac92xx_get_vref(codec, nid); >+ stac92xx_auto_set_pinctl(codec, nid, pinctl); >+ } >+ } >+ for (i = 0; i < spec->num_dmics; i++) >+ stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i], >+ AC_PINCTL_IN_EN); >+ for (i = 0; i < spec->num_pwrs; i++) { >+ int event = is_nid_hp_pin(cfg, spec->pwr_nids[i]) >+ ? STAC_HP_EVENT : STAC_PWR_EVENT; >+ int pinctl = snd_hda_codec_read(codec, spec->pwr_nids[i], >+ 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); >+ int def_conf = snd_hda_codec_read(codec, spec->pwr_nids[i], >+ 0, AC_VERB_GET_CONFIG_DEFAULT, 0); >+ /* outputs are only ports capable of power management >+ * any attempts on powering down a input port cause the >+ * referenced VREF to act quirky. >+ */ >+ if (pinctl & AC_PINCTL_IN_EN) >+ continue; >+ if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) >+ continue; >+ enable_pin_detect(codec, spec->pwr_nids[i], event | i); >+ codec->patch_ops.unsol_event(codec, (event | i) << 26); >+ } >+ if (spec->dac_list) >+ stac92xx_power_down(codec); >+ if (cfg->dig_out_pin) >+ stac92xx_auto_set_pinctl(codec, cfg->dig_out_pin, >+ AC_PINCTL_OUT_EN); >+ if (cfg->dig_in_pin) >+ stac92xx_auto_set_pinctl(codec, cfg->dig_in_pin, >+ AC_PINCTL_IN_EN); >+ >+ stac_gpio_set(codec, spec->gpio_mask, >+ spec->gpio_dir, spec->gpio_data); >+ >+ return 0; >+} >+ >+static void stac92xx_free(struct hda_codec *codec) >+{ >+ struct sigmatel_spec *spec = codec->spec; >+ int i; >+ >+ if (! spec) >+ return; >+ >+ if (spec->kctl_alloc) { >+ for (i = 0; i < spec->num_kctl_used; i++) >+ kfree(spec->kctl_alloc[i].name); >+ kfree(spec->kctl_alloc); >+ } >+ >+ if (spec->bios_pin_configs) >+ kfree(spec->bios_pin_configs); >+ >+ kfree(spec); >+} >+ >+static void stac92xx_set_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 & 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; >+ struct auto_pin_cfg *cfg = &spec->autocfg; >+ if ((nid == cfg->input_pins[AUTO_PIN_LINE] && >+ spec->line_switch) || >+ (nid == cfg->input_pins[AUTO_PIN_MIC] && >+ spec->mic_switch)) >+ return; >+ } >+ >+ /* 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); >+ >+ snd_hda_codec_write_cache(codec, nid, 0, >+ AC_VERB_SET_PIN_WIDGET_CONTROL, >+ pin_ctl | flag); >+} >+ >+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); >+ snd_hda_codec_write_cache(codec, nid, 0, >+ AC_VERB_SET_PIN_WIDGET_CONTROL, >+ pin_ctl & ~flag); >+} >+ >+static int get_hp_pin_presence(struct hda_codec *codec, hda_nid_t nid) >+{ >+ if (!nid) >+ return 0; >+ if (snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0x00) >+ & (1 << 31)) { >+ unsigned int pinctl; >+ pinctl = snd_hda_codec_read(codec, nid, 0, >+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0); >+ if (pinctl & AC_PINCTL_IN_EN) >+ return 0; /* mic- or line-input */ >+ else >+ return 1; /* HP-output */ >+ } >+ return 0; >+} >+ >+static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res) >+{ >+ struct sigmatel_spec *spec = codec->spec; >+ struct auto_pin_cfg *cfg = &spec->autocfg; >+ int nid = cfg->hp_pins[cfg->hp_outs - 1]; >+ int i, presence; >+ >+ presence = 0; >+ 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 (spec->hp_switch && cfg->hp_pins[i] == nid) >+ break; >+ presence = get_hp_pin_presence(codec, cfg->hp_pins[i]); >+ } >+ >+ if (presence) { >+ /* disable lineouts, enable hp */ >+ if (spec->hp_switch) >+ stac92xx_reset_pinctl(codec, nid, AC_PINCTL_OUT_EN); >+ for (i = 0; i < cfg->line_outs; i++) >+ stac92xx_reset_pinctl(codec, cfg->line_out_pins[i], >+ AC_PINCTL_OUT_EN); >+ for (i = 0; i < cfg->speaker_outs; i++) >+ stac92xx_reset_pinctl(codec, cfg->speaker_pins[i], >+ AC_PINCTL_OUT_EN); >+ if (spec->eapd_mask) >+ stac_gpio_set(codec, spec->gpio_mask, >+ spec->gpio_dir, spec->gpio_data & >+ ~spec->eapd_mask); >+ } else { >+ /* enable lineouts, disable hp */ >+ if (spec->hp_switch) >+ stac92xx_set_pinctl(codec, nid, AC_PINCTL_OUT_EN); >+ for (i = 0; i < cfg->line_outs; i++) >+ stac92xx_set_pinctl(codec, cfg->line_out_pins[i], >+ AC_PINCTL_OUT_EN); >+ for (i = 0; i < cfg->speaker_outs; i++) >+ stac92xx_set_pinctl(codec, cfg->speaker_pins[i], >+ AC_PINCTL_OUT_EN); >+ if (spec->eapd_mask) >+ stac_gpio_set(codec, spec->gpio_mask, >+ spec->gpio_dir, spec->gpio_data | >+ spec->eapd_mask); >+ } >+ if (!spec->hp_switch && cfg->hp_outs > 1 && presence) >+ stac92xx_set_pinctl(codec, nid, AC_PINCTL_OUT_EN); >+} >+ >+static void stac92xx_pin_sense(struct hda_codec *codec, int idx) >+{ >+ struct sigmatel_spec *spec = codec->spec; >+ hda_nid_t nid = spec->pwr_nids[idx]; >+ int presence, val; >+ val = snd_hda_codec_read(codec, codec->afg, 0, 0x0fec, 0x0) >+ & 0x000000ff; >+ presence = get_hp_pin_presence(codec, nid); >+ idx = 1 << idx; >+ >+ if (presence) >+ val &= ~idx; >+ else >+ val |= idx; >+ >+ /* power down unused output ports */ >+ snd_hda_codec_write(codec, codec->afg, 0, 0x7ec, val); >+}; >+ >+static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res) >+{ >+ struct sigmatel_spec *spec = codec->spec; >+ int idx = res >> 26 & 0x0f; >+ >+ switch ((res >> 26) & 0x30) { >+ case STAC_HP_EVENT: >+ stac92xx_hp_detect(codec, res); >+ /* fallthru */ >+ case STAC_PWR_EVENT: >+ if (spec->num_pwrs > 0) >+ stac92xx_pin_sense(codec, idx); >+ } >+} >+ >+#ifdef SND_HDA_NEEDS_RESUME >+static int stac92xx_resume(struct hda_codec *codec) >+{ >+ struct sigmatel_spec *spec = codec->spec; >+ >+ stac92xx_set_config_regs(codec); >+ snd_hda_sequence_write(codec, spec->init); >+ stac_gpio_set(codec, spec->gpio_mask, >+ spec->gpio_dir, spec->gpio_data); >+ snd_hda_codec_resume_amp(codec); >+ snd_hda_codec_resume_cache(codec); >+ /* power down inactive DACs */ >+ if (spec->dac_list) >+ stac92xx_power_down(codec); >+ /* invoke unsolicited event to reset the HP state */ >+ if (spec->hp_detect) >+ codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26); >+ return 0; >+} >+#endif >+ >+static 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 SND_HDA_NEEDS_RESUME >+ .resume = stac92xx_resume, >+#endif >+}; >+ >+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->spec = spec; >+ 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: Unknown model for STAC9200, using BIOS defaults\n"); >+ err = stac92xx_save_bios_config_regs(codec); >+ if (err < 0) { >+ stac92xx_free(codec); >+ return err; >+ } >+ spec->pin_configs = spec->bios_pin_configs; >+ } else { >+ spec->pin_configs = stac9200_brd_tbl[spec->board_config]; >+ stac92xx_set_config_regs(codec); >+ } >+ >+ 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_GATEWAY || >+ spec->board_config == STAC_9200_OQO) >+ spec->init = stac9200_eapd_init; >+ else >+ spec->init = stac9200_core_init; >+ spec->mixer = stac9200_mixer; >+ >+ if (spec->board_config == STAC_9200_PANASONIC) { >+ spec->gpio_mask = spec->gpio_dir = 0x09; >+ spec->gpio_data = 0x00; >+ } >+ >+ err = stac9200_parse_auto_config(codec); >+ if (err < 0) { >+ stac92xx_free(codec); >+ return err; >+ } >+ >+ 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->spec = spec; >+ spec->num_pins = ARRAY_SIZE(stac925x_pin_nids); >+ spec->pin_nids = stac925x_pin_nids; >+ 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: Unknown model for STAC925x," >+ "using BIOS defaults\n"); >+ err = stac92xx_save_bios_config_regs(codec); >+ if (err < 0) { >+ stac92xx_free(codec); >+ return err; >+ } >+ spec->pin_configs = spec->bios_pin_configs; >+ } else if (stac925x_brd_tbl[spec->board_config] != NULL){ >+ spec->pin_configs = stac925x_brd_tbl[spec->board_config]; >+ stac92xx_set_config_regs(codec); >+ } >+ >+ 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; >+ >+ err = stac92xx_parse_auto_config(codec, 0x8, 0x7); >+ 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 struct hda_input_mux stac92hd73xx_dmux = { >+ .num_items = 4, >+ .items = { >+ { "Analog Inputs", 0x0b }, >+ { "CD", 0x08 }, >+ { "Digital Mic 1", 0x09 }, >+ { "Digital Mic 2", 0x0a }, >+ } >+}; >+ >+static int patch_stac92hd73xx(struct hda_codec *codec) >+{ >+ struct sigmatel_spec *spec; >+ hda_nid_t conn[STAC92HD73_DAC_COUNT + 2]; >+ int err = 0; >+ >+ spec = kzalloc(sizeof(*spec), GFP_KERNEL); >+ if (spec == NULL) >+ return -ENOMEM; >+ >+ codec->spec = spec; >+ 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); >+again: >+ if (spec->board_config < 0) { >+ snd_printdd(KERN_INFO "hda_codec: Unknown model for" >+ " STAC92HD73XX, using BIOS defaults\n"); >+ err = stac92xx_save_bios_config_regs(codec); >+ if (err < 0) { >+ stac92xx_free(codec); >+ return err; >+ } >+ spec->pin_configs = spec->bios_pin_configs; >+ } else { >+ spec->pin_configs = stac92hd73xx_brd_tbl[spec->board_config]; >+ stac92xx_set_config_regs(codec); >+ } >+ >+ spec->multiout.num_dacs = snd_hda_get_connections(codec, 0x0a, >+ conn, STAC92HD73_DAC_COUNT + 2) - 1; >+ >+ if (spec->multiout.num_dacs < 0) { >+ printk(KERN_WARNING "hda_codec: Could not determine " >+ "number of channels defaulting to DAC count\n"); >+ spec->multiout.num_dacs = STAC92HD73_DAC_COUNT; >+ } >+ >+ switch (spec->multiout.num_dacs) { >+ case 0x3: /* 6 Channel */ >+ spec->multiout.hp_nid = 0x17; >+ spec->mixer = stac92hd73xx_6ch_mixer; >+ spec->init = stac92hd73xx_6ch_core_init; >+ break; >+ case 0x4: /* 8 Channel */ >+ spec->multiout.hp_nid = 0x18; >+ spec->mixer = stac92hd73xx_8ch_mixer; >+ spec->init = stac92hd73xx_8ch_core_init; >+ break; >+ case 0x5: /* 10 Channel */ >+ spec->multiout.hp_nid = 0x19; >+ spec->mixer = stac92hd73xx_10ch_mixer; >+ spec->init = stac92hd73xx_10ch_core_init; >+ }; >+ >+ spec->multiout.dac_nids = stac92hd73xx_dac_nids; >+ spec->aloopback_mask = 0x01; >+ spec->aloopback_shift = 8; >+ >+ 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->num_muxes = ARRAY_SIZE(stac92hd73xx_mux_nids); >+ spec->num_adcs = ARRAY_SIZE(stac92hd73xx_adc_nids); >+ spec->num_dmuxes = ARRAY_SIZE(stac92hd73xx_dmux_nids); >+ spec->dinput_mux = &stac92hd73xx_dmux; >+ /* GPIO0 High = Enable EAPD */ >+ spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1; >+ spec->gpio_data = 0x01; >+ >+ switch (spec->board_config) { >+ case STAC_DELL_M6: >+ spec->init = dell_eq_core_init; >+ switch (codec->subsystem_id) { >+ case 0x1028025e: /* Analog Mics */ >+ case 0x1028025f: >+ stac92xx_set_config_reg(codec, 0x0b, 0x90A70170); >+ spec->num_dmics = 0; >+ break; >+ case 0x10280271: /* Digital Mics */ >+ case 0x10280272: >+ spec->init = dell_m6_core_init; >+ /* fall-through */ >+ case 0x10280254: >+ case 0x10280255: >+ stac92xx_set_config_reg(codec, 0x13, 0x90A60160); >+ spec->num_dmics = 1; >+ break; >+ case 0x10280256: /* Both */ >+ case 0x10280057: >+ stac92xx_set_config_reg(codec, 0x0b, 0x90A70170); >+ stac92xx_set_config_reg(codec, 0x13, 0x90A60160); >+ spec->num_dmics = 1; >+ break; >+ } >+ break; >+ default: >+ spec->num_dmics = STAC92HD73XX_NUM_DMICS; >+ } >+ >+ spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids); >+ spec->pwr_nids = stac92hd73xx_pwr_nids; >+ >+ err = stac92xx_parse_auto_config(codec, 0x22, 0x24); >+ >+ 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; >+ } >+ >+ codec->patch_ops = stac92xx_patch_ops; >+ >+ return 0; >+} >+ >+static int patch_stac92hd71bxx(struct hda_codec *codec) >+{ >+ struct sigmatel_spec *spec; >+ int err = 0; >+ >+ spec = kzalloc(sizeof(*spec), GFP_KERNEL); >+ if (spec == NULL) >+ return -ENOMEM; >+ >+ codec->spec = spec; >+ spec->num_pins = ARRAY_SIZE(stac92hd71bxx_pin_nids); >+ spec->pin_nids = stac92hd71bxx_pin_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: Unknown model for" >+ " STAC92HD71BXX, using BIOS defaults\n"); >+ err = stac92xx_save_bios_config_regs(codec); >+ if (err < 0) { >+ stac92xx_free(codec); >+ return err; >+ } >+ spec->pin_configs = spec->bios_pin_configs; >+ } else { >+ spec->pin_configs = stac92hd71bxx_brd_tbl[spec->board_config]; >+ stac92xx_set_config_regs(codec); >+ } >+ >+ switch (codec->vendor_id) { >+ case 0x111d76b6: /* 4 Port without Analog Mixer */ >+ case 0x111d76b7: >+ case 0x111d76b4: /* 6 Port without Analog Mixer */ >+ case 0x111d76b5: >+ spec->mixer = stac92hd71bxx_mixer; >+ spec->init = stac92hd71bxx_core_init; >+ break; >+ default: >+ spec->mixer = stac92hd71bxx_analog_mixer; >+ spec->init = stac92hd71bxx_analog_core_init; >+ } >+ >+ spec->aloopback_mask = 0x20; >+ spec->aloopback_shift = 0; >+ >+ /* GPIO0 High = EAPD */ >+ spec->gpio_mask = 0x01; >+ spec->gpio_dir = 0x01; >+ spec->gpio_mask = 0x01; >+ spec->gpio_data = 0x01; >+ >+ spec->mux_nids = stac92hd71bxx_mux_nids; >+ spec->adc_nids = stac92hd71bxx_adc_nids; >+ spec->dmic_nids = stac92hd71bxx_dmic_nids; >+ spec->dmux_nids = stac92hd71bxx_dmux_nids; >+ >+ spec->num_muxes = ARRAY_SIZE(stac92hd71bxx_mux_nids); >+ spec->num_adcs = ARRAY_SIZE(stac92hd71bxx_adc_nids); >+ spec->num_dmics = STAC92HD71BXX_NUM_DMICS; >+ spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids); >+ >+ spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids); >+ spec->pwr_nids = stac92hd71bxx_pwr_nids; >+ >+ spec->multiout.num_dacs = 1; >+ spec->multiout.hp_nid = 0x11; >+ spec->multiout.dac_nids = stac92hd71bxx_dac_nids; >+ >+ err = stac92xx_parse_auto_config(codec, 0x21, 0x23); >+ 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; >+ } >+ >+ codec->patch_ops = stac92xx_patch_ops; >+ >+ 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->spec = spec; >+ 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_V3) { >+ 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; >+ } >+ } >+ >+ again: >+ if (spec->board_config < 0) { >+ snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, " >+ "using BIOS defaults\n"); >+ err = stac92xx_save_bios_config_regs(codec); >+ if (err < 0) { >+ stac92xx_free(codec); >+ return err; >+ } >+ spec->pin_configs = spec->bios_pin_configs; >+ } else if (stac922x_brd_tbl[spec->board_config] != NULL) { >+ spec->pin_configs = stac922x_brd_tbl[spec->board_config]; >+ stac92xx_set_config_regs(codec); >+ } >+ >+ 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->mixer = stac922x_mixer; >+ >+ spec->multiout.dac_nids = spec->dac_nids; >+ >+ err = stac92xx_parse_auto_config(codec, 0x08, 0x09); >+ 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->spec = spec; >+ 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 || !stac927x_brd_tbl[spec->board_config]) { >+ if (spec->board_config < 0) >+ snd_printdd(KERN_INFO "hda_codec: Unknown model for" >+ "STAC927x, using BIOS defaults\n"); >+ err = stac92xx_save_bios_config_regs(codec); >+ if (err < 0) { >+ stac92xx_free(codec); >+ return err; >+ } >+ spec->pin_configs = spec->bios_pin_configs; >+ } else { >+ spec->pin_configs = stac927x_brd_tbl[spec->board_config]; >+ stac92xx_set_config_regs(codec); >+ } >+ >+ 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->dac_list = stac927x_dac_nids; >+ spec->multiout.dac_nids = spec->dac_nids; >+ >+ switch (spec->board_config) { >+ case STAC_D965_3ST: >+ case STAC_D965_5ST: >+ /* GPIO0 High = Enable EAPD */ >+ spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x01; >+ spec->gpio_data = 0x01; >+ spec->num_dmics = 0; >+ >+ spec->init = d965_core_init; >+ spec->mixer = stac927x_mixer; >+ break; >+ case STAC_DELL_BIOS: >+ switch (codec->subsystem_id) { >+ case 0x10280209: >+ case 0x1028022e: >+ /* correct the device field to SPDIF out */ >+ stac92xx_set_config_reg(codec, 0x21, 0x01442070); >+ break; >+ }; >+ /* configure the analog microphone on some laptops */ >+ stac92xx_set_config_reg(codec, 0x0c, 0x90a79130); >+ /* correct the front output jack as a hp out */ >+ stac92xx_set_config_reg(codec, 0x0f, 0x0227011f); >+ /* correct the front input jack as a mic */ >+ stac92xx_set_config_reg(codec, 0x0e, 0x02a79130); >+ /* fallthru */ >+ case STAC_DELL_3ST: >+ /* GPIO2 High = Enable EAPD */ >+ spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x04; >+ spec->gpio_data = 0x04; >+ spec->dmic_nids = stac927x_dmic_nids; >+ spec->num_dmics = STAC927X_NUM_DMICS; >+ >+ spec->init = d965_core_init; >+ spec->mixer = stac927x_mixer; >+ spec->dmux_nids = stac927x_dmux_nids; >+ spec->num_dmuxes = ARRAY_SIZE(stac927x_dmux_nids); >+ break; >+ default: >+ /* GPIO0 High = Enable EAPD */ >+ spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1; >+ spec->gpio_data = 0x01; >+ spec->num_dmics = 0; >+ >+ spec->init = stac927x_core_init; >+ spec->mixer = stac927x_mixer; >+ } >+ >+ spec->num_pwrs = 0; >+ spec->aloopback_mask = 0x40; >+ spec->aloopback_shift = 0; >+ >+ err = stac92xx_parse_auto_config(codec, 0x1e, 0x20); >+ 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; >+ >+ /* >+ * !!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; >+ >+ 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->spec = spec; >+ 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: Unknown model for STAC9205, using BIOS defaults\n"); >+ err = stac92xx_save_bios_config_regs(codec); >+ if (err < 0) { >+ stac92xx_free(codec); >+ return err; >+ } >+ spec->pin_configs = spec->bios_pin_configs; >+ } else { >+ spec->pin_configs = stac9205_brd_tbl[spec->board_config]; >+ stac92xx_set_config_regs(codec); >+ } >+ >+ 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->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->mixer = stac9205_mixer; >+ >+ spec->aloopback_mask = 0x40; >+ spec->aloopback_shift = 0; >+ spec->multiout.dac_nids = spec->dac_nids; >+ >+ switch (spec->board_config){ >+ case STAC_9205_DELL_M43: >+ /* Enable SPDIF in/out */ >+ stac92xx_set_config_reg(codec, 0x1f, 0x01441030); >+ stac92xx_set_config_reg(codec, 0x20, 0x1c410030); >+ >+ /* Enable unsol response for GPIO4/Dock HP connection */ >+ snd_hda_codec_write(codec, codec->afg, 0, >+ AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10); >+ snd_hda_codec_write_cache(codec, codec->afg, 0, >+ AC_VERB_SET_UNSOLICITED_ENABLE, >+ (AC_USRSP_EN | STAC_HP_EVENT)); >+ >+ 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; >+ 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, 0x1f, 0x20); >+ 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; >+ >+ return 0; >+} >+ >+/* >+ * STAC9872 hack >+ */ >+ >+/* static config for Sony VAIO FE550G and Sony VAIO AR */ >+static hda_nid_t vaio_dacs[] = { 0x2 }; >+#define VAIO_HP_DAC 0x5 >+static hda_nid_t vaio_adcs[] = { 0x8 /*,0x6*/ }; >+static hda_nid_t vaio_mux_nids[] = { 0x15 }; >+ >+static struct hda_input_mux vaio_mux = { >+ .num_items = 3, >+ .items = { >+ /* { "HP", 0x0 }, */ >+ { "Mic Jack", 0x1 }, >+ { "Internal Mic", 0x2 }, >+ { "PCM", 0x3 }, >+ } >+}; >+ >+static struct hda_verb vaio_init[] = { >+ {0x0a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP <- 0x2 */ >+ {0x0a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | STAC_HP_EVENT}, >+ {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Speaker <- 0x5 */ >+ {0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? (<- 0x2) */ >+ {0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? */ >+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mic-sel: 0a,0d,14,02 */ >+ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* HP */ >+ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */ >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* capture sw/vol -> 0x8 */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* CD-in -> 0x6 */ >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Mic-in -> 0x9 */ >+ {} >+}; >+ >+static struct hda_verb vaio_ar_init[] = { >+ {0x0a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP <- 0x2 */ >+ {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Speaker <- 0x5 */ >+ {0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? (<- 0x2) */ >+ {0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */ >+/* {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },*/ /* Optical Out */ >+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? */ >+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mic-sel: 0a,0d,14,02 */ >+ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* HP */ >+ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */ >+/* {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},*/ /* Optical Out */ >+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* capture sw/vol -> 0x8 */ >+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* CD-in -> 0x6 */ >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Mic-in -> 0x9 */ >+ {} >+}; >+ >+/* bind volumes of both NID 0x02 and 0x05 */ >+static struct hda_bind_ctls vaio_bind_master_vol = { >+ .ops = &snd_hda_bind_vol, >+ .values = { >+ HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT), >+ HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT), >+ 0 >+ }, >+}; >+ >+/* bind volumes of both NID 0x02 and 0x05 */ >+static struct hda_bind_ctls vaio_bind_master_sw = { >+ .ops = &snd_hda_bind_sw, >+ .values = { >+ HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT), >+ HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT), >+ 0, >+ }, >+}; >+ >+static struct snd_kcontrol_new vaio_mixer[] = { >+ HDA_BIND_VOL("Master Playback Volume", &vaio_bind_master_vol), >+ HDA_BIND_SW("Master Playback Switch", &vaio_bind_master_sw), >+ /* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */ >+ HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Capture Source", >+ .count = 1, >+ .info = stac92xx_mux_enum_info, >+ .get = stac92xx_mux_enum_get, >+ .put = stac92xx_mux_enum_put, >+ }, >+ {} >+}; >+ >+static struct snd_kcontrol_new vaio_ar_mixer[] = { >+ HDA_BIND_VOL("Master Playback Volume", &vaio_bind_master_vol), >+ HDA_BIND_SW("Master Playback Switch", &vaio_bind_master_sw), >+ /* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */ >+ HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT), >+ /*HDA_CODEC_MUTE("Optical Out Switch", 0x10, 0, HDA_OUTPUT), >+ HDA_CODEC_VOLUME("Optical Out Volume", 0x10, 0, HDA_OUTPUT),*/ >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ .name = "Capture Source", >+ .count = 1, >+ .info = stac92xx_mux_enum_info, >+ .get = stac92xx_mux_enum_get, >+ .put = stac92xx_mux_enum_put, >+ }, >+ {} >+}; >+ >+static struct hda_codec_ops stac9872_patch_ops = { >+ .build_controls = stac92xx_build_controls, >+ .build_pcms = stac92xx_build_pcms, >+ .init = stac92xx_init, >+ .free = stac92xx_free, >+#ifdef SND_HDA_NEEDS_RESUME >+ .resume = stac92xx_resume, >+#endif >+}; >+ >+static int stac9872_vaio_init(struct hda_codec *codec) >+{ >+ int err; >+ >+ err = stac92xx_init(codec); >+ if (err < 0) >+ return err; >+ if (codec->patch_ops.unsol_event) >+ codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26); >+ return 0; >+} >+ >+static void stac9872_vaio_hp_detect(struct hda_codec *codec, unsigned int res) >+{ >+ if (get_hp_pin_presence(codec, 0x0a)) { >+ stac92xx_reset_pinctl(codec, 0x0f, AC_PINCTL_OUT_EN); >+ stac92xx_set_pinctl(codec, 0x0a, AC_PINCTL_OUT_EN); >+ } else { >+ stac92xx_reset_pinctl(codec, 0x0a, AC_PINCTL_OUT_EN); >+ stac92xx_set_pinctl(codec, 0x0f, AC_PINCTL_OUT_EN); >+ } >+} >+ >+static void stac9872_vaio_unsol_event(struct hda_codec *codec, unsigned int res) >+{ >+ switch (res >> 26) { >+ case STAC_HP_EVENT: >+ stac9872_vaio_hp_detect(codec, res); >+ break; >+ } >+} >+ >+static struct hda_codec_ops stac9872_vaio_patch_ops = { >+ .build_controls = stac92xx_build_controls, >+ .build_pcms = stac92xx_build_pcms, >+ .init = stac9872_vaio_init, >+ .free = stac92xx_free, >+ .unsol_event = stac9872_vaio_unsol_event, >+#ifdef CONFIG_PM >+ .resume = stac92xx_resume, >+#endif >+}; >+ >+enum { /* FE and SZ series. id=0x83847661 and subsys=0x104D0700 or 104D1000. */ >+ CXD9872RD_VAIO, >+ /* Unknown. id=0x83847662 and subsys=0x104D1200 or 104D1000. */ >+ STAC9872AK_VAIO, >+ /* Unknown. id=0x83847661 and subsys=0x104D1200. */ >+ STAC9872K_VAIO, >+ /* AR Series. id=0x83847664 and subsys=104D1300 */ >+ CXD9872AKD_VAIO, >+ STAC_9872_MODELS, >+}; >+ >+static const char *stac9872_models[STAC_9872_MODELS] = { >+ [CXD9872RD_VAIO] = "vaio", >+ [CXD9872AKD_VAIO] = "vaio-ar", >+}; >+ >+static struct snd_pci_quirk stac9872_cfg_tbl[] = { >+ SND_PCI_QUIRK(0x104d, 0x81e6, "Sony VAIO F/S", CXD9872RD_VAIO), >+ SND_PCI_QUIRK(0x104d, 0x81ef, "Sony VAIO F/S", CXD9872RD_VAIO), >+ SND_PCI_QUIRK(0x104d, 0x81fd, "Sony VAIO AR", CXD9872AKD_VAIO), >+ SND_PCI_QUIRK(0x104d, 0x8205, "Sony VAIO AR", CXD9872AKD_VAIO), >+ {} >+}; >+ >+static int patch_stac9872(struct hda_codec *codec) >+{ >+ struct sigmatel_spec *spec; >+ int board_config; >+ >+ board_config = snd_hda_check_board_config(codec, STAC_9872_MODELS, >+ stac9872_models, >+ stac9872_cfg_tbl); >+ if (board_config < 0) >+ /* unknown config, let generic-parser do its job... */ >+ return snd_hda_parse_generic_codec(codec); >+ >+ spec = kzalloc(sizeof(*spec), GFP_KERNEL); >+ if (spec == NULL) >+ return -ENOMEM; >+ >+ codec->spec = spec; >+ switch (board_config) { >+ case CXD9872RD_VAIO: >+ case STAC9872AK_VAIO: >+ case STAC9872K_VAIO: >+ spec->mixer = vaio_mixer; >+ spec->init = vaio_init; >+ spec->multiout.max_channels = 2; >+ spec->multiout.num_dacs = ARRAY_SIZE(vaio_dacs); >+ spec->multiout.dac_nids = vaio_dacs; >+ spec->multiout.hp_nid = VAIO_HP_DAC; >+ spec->num_adcs = ARRAY_SIZE(vaio_adcs); >+ spec->adc_nids = vaio_adcs; >+ spec->num_pwrs = 0; >+ spec->input_mux = &vaio_mux; >+ spec->mux_nids = vaio_mux_nids; >+ codec->patch_ops = stac9872_vaio_patch_ops; >+ break; >+ >+ case CXD9872AKD_VAIO: >+ spec->mixer = vaio_ar_mixer; >+ spec->init = vaio_ar_init; >+ spec->multiout.max_channels = 2; >+ spec->multiout.num_dacs = ARRAY_SIZE(vaio_dacs); >+ spec->multiout.dac_nids = vaio_dacs; >+ spec->multiout.hp_nid = VAIO_HP_DAC; >+ spec->num_adcs = ARRAY_SIZE(vaio_adcs); >+ spec->num_pwrs = 0; >+ spec->adc_nids = vaio_adcs; >+ spec->input_mux = &vaio_mux; >+ spec->mux_nids = vaio_mux_nids; >+ codec->patch_ops = stac9872_patch_ops; >+ break; >+ } >+ >+ return 0; >+} >+ >+ >+/* >+ * patch entries >+ */ >+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 = 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 = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx }, >+ { .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx }, >+ { .id = 0x111d7676, .name = "92HD73E1X5", .patch = patch_stac92hd73xx }, >+ { .id = 0x111d7608, .name = "92HD71BXX", .patch = patch_stac92hd71bxx }, >+ { .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 }, >+ {} /* terminator */ >+}; >diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c >new file mode 100644 >index 0000000..9ebbe68 >--- /dev/null >+++ b/sound/pci/hda/patch_via.c >@@ -0,0 +1,2027 @@ >+/* >+ * Universal Interface for Intel High Definition Audio Codec >+ * >+ * HD audio interface patch for VIA VT1708 codec >+ * >+ * Copyright (c) 2006 Lydia Wang <lydiawang@viatech.com> >+ * 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 */ >+/* */ >+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ >+ >+ >+#include <sound/driver.h> >+#include <linux/init.h> >+#include <linux/delay.h> >+#include <linux/slab.h> >+#include <sound/core.h> >+#include "hda_codec.h" >+#include "hda_local.h" >+#include "hda_patch.h" >+ >+/* amp values */ >+#define AMP_VAL_IDX_SHIFT 19 >+#define AMP_VAL_IDX_MASK (0x0f<<19) >+ >+#define NUM_CONTROL_ALLOC 32 >+#define NUM_VERB_ALLOC 32 >+ >+/* Pin Widget NID */ >+#define VT1708_HP_NID 0x13 >+#define VT1708_DIGOUT_NID 0x14 >+#define VT1708_DIGIN_NID 0x16 >+#define VT1708_DIGIN_PIN 0x26 >+ >+#define VT1709_HP_DAC_NID 0x28 >+#define VT1709_DIGOUT_NID 0x13 >+#define VT1709_DIGIN_NID 0x17 >+#define VT1709_DIGIN_PIN 0x25 >+ >+#define VT1708B_HP_NID 0x25 >+#define VT1708B_DIGOUT_NID 0x12 >+#define VT1708B_DIGIN_NID 0x15 >+#define VT1708B_DIGIN_PIN 0x21 >+ >+#define IS_VT1708_VENDORID(x) ((x) >= 0x11061708 && (x) <= 0x1106170b) >+#define IS_VT1709_10CH_VENDORID(x) ((x) >= 0x1106e710 && (x) <= 0x1106e713) >+#define IS_VT1709_6CH_VENDORID(x) ((x) >= 0x1106e714 && (x) <= 0x1106e717) >+#define IS_VT1708B_8CH_VENDORID(x) ((x) >= 0x1106e720 && (x) <= 0x1106e723) >+#define IS_VT1708B_4CH_VENDORID(x) ((x) >= 0x1106e724 && (x) <= 0x1106e727) >+ >+ >+enum { >+ VIA_CTL_WIDGET_VOL, >+ VIA_CTL_WIDGET_MUTE, >+}; >+ >+enum { >+ AUTO_SEQ_FRONT, >+ AUTO_SEQ_SURROUND, >+ AUTO_SEQ_CENLFE, >+ AUTO_SEQ_SIDE >+}; >+ >+static struct snd_kcontrol_new vt1708_control_templates[] = { >+ HDA_CODEC_VOLUME(NULL, 0, 0, 0), >+ HDA_CODEC_MUTE(NULL, 0, 0, 0), >+}; >+ >+ >+struct via_spec { >+ /* codec parameterization */ >+ struct snd_kcontrol_new *mixers[3]; >+ unsigned int num_mixers; >+ >+ struct hda_verb *init_verbs; >+ >+ char *stream_name_analog; >+ struct hda_pcm_stream *stream_analog_playback; >+ struct hda_pcm_stream *stream_analog_capture; >+ >+ char *stream_name_digital; >+ struct hda_pcm_stream *stream_digital_playback; >+ struct hda_pcm_stream *stream_digital_capture; >+ >+ /* playback */ >+ struct hda_multi_out multiout; >+ >+ /* capture */ >+ unsigned int num_adc_nids; >+ hda_nid_t *adc_nids; >+ hda_nid_t dig_in_nid; >+ >+ /* capture source */ >+ const struct hda_input_mux *input_mux; >+ unsigned int cur_mux[3]; >+ >+ /* PCM information */ >+ struct hda_pcm pcm_rec[2]; >+ >+ /* dynamic controls, init_verbs and input_mux */ >+ struct auto_pin_cfg autocfg; >+ unsigned int num_kctl_alloc, num_kctl_used; >+ struct snd_kcontrol_new *kctl_alloc; >+ struct hda_input_mux private_imux; >+ hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; >+ >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+ struct hda_loopback_check loopback; >+#endif >+}; >+ >+static hda_nid_t vt1708_adc_nids[2] = { >+ /* ADC1-2 */ >+ 0x15, 0x27 >+}; >+ >+static hda_nid_t vt1709_adc_nids[3] = { >+ /* ADC1-2 */ >+ 0x14, 0x15, 0x16 >+}; >+ >+static hda_nid_t vt1708B_adc_nids[2] = { >+ /* ADC1-2 */ >+ 0x13, 0x14 >+}; >+ >+/* add dynamic controls */ >+static int via_add_control(struct via_spec *spec, int type, const char *name, >+ unsigned long val) >+{ >+ struct snd_kcontrol_new *knew; >+ >+ if (spec->num_kctl_used >= spec->num_kctl_alloc) { >+ int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC; >+ >+ /* array + terminator */ >+ knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); >+ if (!knew) >+ return -ENOMEM; >+ if (spec->kctl_alloc) { >+ memcpy(knew, spec->kctl_alloc, >+ sizeof(*knew) * spec->num_kctl_alloc); >+ kfree(spec->kctl_alloc); >+ } >+ spec->kctl_alloc = knew; >+ spec->num_kctl_alloc = num; >+ } >+ >+ knew = &spec->kctl_alloc[spec->num_kctl_used]; >+ *knew = vt1708_control_templates[type]; >+ knew->name = kstrdup(name, GFP_KERNEL); >+ >+ if (!knew->name) >+ return -ENOMEM; >+ knew->private_value = val; >+ spec->num_kctl_used++; >+ return 0; >+} >+ >+/* create input playback/capture controls for the given pin */ >+static int via_new_analog_input(struct via_spec *spec, hda_nid_t pin, >+ const char *ctlname, int idx, int mix_nid) >+{ >+ char name[32]; >+ int err; >+ >+ sprintf(name, "%s Playback Volume", ctlname); >+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, >+ 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_MUTE, name, >+ HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); >+ if (err < 0) >+ return err; >+ return 0; >+} >+ >+static void via_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); >+} >+ >+ >+static void via_auto_init_multi_out(struct hda_codec *codec) >+{ >+ struct via_spec *spec = codec->spec; >+ int i; >+ >+ for (i = 0; i <= AUTO_SEQ_SIDE; i++) { >+ hda_nid_t nid = spec->autocfg.line_out_pins[i]; >+ if (nid) >+ via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i); >+ } >+} >+ >+static void via_auto_init_hp_out(struct hda_codec *codec) >+{ >+ struct via_spec *spec = codec->spec; >+ hda_nid_t pin; >+ >+ pin = spec->autocfg.hp_pins[0]; >+ if (pin) /* connect to front */ >+ via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); >+} >+ >+static void via_auto_init_analog_input(struct hda_codec *codec) >+{ >+ struct via_spec *spec = codec->spec; >+ int i; >+ >+ for (i = 0; i < AUTO_PIN_LAST; i++) { >+ hda_nid_t nid = spec->autocfg.input_pins[i]; >+ >+ snd_hda_codec_write(codec, nid, 0, >+ AC_VERB_SET_PIN_WIDGET_CONTROL, >+ (i <= AUTO_PIN_FRONT_MIC ? >+ PIN_VREF50 : PIN_IN)); >+ >+ } >+} >+/* >+ * input MUX handling >+ */ >+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; >+ return snd_hda_input_mux_info(spec->input_mux, uinfo); >+} >+ >+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 adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); >+ >+ ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_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 adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); >+ unsigned int vendor_id = codec->vendor_id; >+ >+ /* AIW0 lydia 060801 add for correct sw0 input select */ >+ if (IS_VT1708_VENDORID(vendor_id) && (adc_idx == 0)) >+ return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, >+ 0x18, &spec->cur_mux[adc_idx]); >+ else if ((IS_VT1709_10CH_VENDORID(vendor_id) || >+ IS_VT1709_6CH_VENDORID(vendor_id)) && adc_idx == 0) >+ return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, >+ 0x19, &spec->cur_mux[adc_idx]); >+ else if ((IS_VT1708B_8CH_VENDORID(vendor_id) || >+ IS_VT1708B_4CH_VENDORID(vendor_id)) && adc_idx == 0) >+ return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, >+ 0x17, &spec->cur_mux[adc_idx]); >+ else >+ return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, >+ spec->adc_nids[adc_idx], >+ &spec->cur_mux[adc_idx]); >+} >+ >+/* capture mixer elements */ >+static struct snd_kcontrol_new vt1708_capture_mixer[] = { >+ HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ /* The multiple "Capture Source" controls confuse alsamixer >+ * So call somewhat different.. >+ */ >+ /* .name = "Capture Source", */ >+ .name = "Input Source", >+ .count = 1, >+ .info = via_mux_enum_info, >+ .get = via_mux_enum_get, >+ .put = via_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+/* >+ * generic initialization of ADC, input mixers and output mixers >+ */ >+static struct hda_verb vt1708_volume_init_verbs[] = { >+ /* >+ * Unmute ADC0-1 and set the default input to mic-in >+ */ >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ >+ >+ /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback >+ * mixer widget >+ */ >+ /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ >+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, >+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, >+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, >+ >+ /* >+ * Set up output mixers (0x19 - 0x1b) >+ */ >+ /* set vol=0 to output mixers */ >+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ >+ /* Setup default input to PW4 */ >+ {0x20, AC_VERB_SET_CONNECT_SEL, 0x1}, >+ /* PW9 Output enable */ >+ {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, >+ { } >+}; >+ >+static int via_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_analog_open(codec, &spec->multiout, substream, >+ hinfo); >+} >+ >+static int via_playback_pcm_prepare(struct hda_pcm_stream *hinfo, >+ struct hda_codec *codec, >+ unsigned int stream_tag, >+ unsigned int format, >+ struct snd_pcm_substream *substream) >+{ >+ struct via_spec *spec = codec->spec; >+ return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, >+ stream_tag, format, substream); >+} >+ >+static int via_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, >+ struct hda_codec *codec, >+ struct snd_pcm_substream *substream) >+{ >+ struct via_spec *spec = codec->spec; >+ return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); >+} >+ >+/* >+ * 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); >+} >+ >+/* >+ * 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; >+} >+ >+static struct hda_pcm_stream vt1708_pcm_analog_playback = { >+ .substreams = 1, >+ .channels_min = 2, >+ .channels_max = 8, >+ .nid = 0x10, /* NID to query formats and rates */ >+ .ops = { >+ .open = via_playback_pcm_open, >+ .prepare = via_playback_pcm_prepare, >+ .cleanup = via_playback_pcm_cleanup >+ }, >+}; >+ >+static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { >+ .substreams = 1, >+ .channels_min = 2, >+ .channels_max = 8, >+ .nid = 0x10, /* NID to query formats and rates */ >+ /* 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_pcm_open, >+ .prepare = via_playback_pcm_prepare, >+ .cleanup = via_playback_pcm_cleanup >+ }, >+}; >+ >+static struct hda_pcm_stream vt1708_pcm_analog_capture = { >+ .substreams = 2, >+ .channels_min = 2, >+ .channels_max = 2, >+ .nid = 0x15, /* NID to query formats and rates */ >+ .ops = { >+ .prepare = via_capture_pcm_prepare, >+ .cleanup = via_capture_pcm_cleanup >+ }, >+}; >+ >+static struct hda_pcm_stream vt1708_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 >+ }, >+}; >+ >+static struct hda_pcm_stream vt1708_pcm_digital_capture = { >+ .substreams = 1, >+ .channels_min = 2, >+ .channels_max = 2, >+}; >+ >+static int via_build_controls(struct hda_codec *codec) >+{ >+ struct via_spec *spec = codec->spec; >+ int err; >+ int i; >+ >+ 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); >+ 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; >+ } >+ 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 = 1; >+ codec->pcm_info = info; >+ >+ info->name = spec->stream_name_analog; >+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback); >+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; >+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture); >+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; >+ >+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = >+ spec->multiout.max_channels; >+ >+ if (spec->multiout.dig_out_nid || spec->dig_in_nid) { >+ codec->num_pcms++; >+ info++; >+ info->name = spec->stream_name_digital; >+ info->pcm_type = HDA_PCM_TYPE_SPDIF; >+ if (spec->multiout.dig_out_nid) { >+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = >+ *(spec->stream_digital_playback); >+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = >+ spec->multiout.dig_out_nid; >+ } >+ if (spec->dig_in_nid) { >+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = >+ *(spec->stream_digital_capture); >+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = >+ spec->dig_in_nid; >+ } >+ } >+ >+ return 0; >+} >+ >+static void via_free(struct hda_codec *codec) >+{ >+ struct via_spec *spec = codec->spec; >+ unsigned int i; >+ >+ if (!spec) >+ return; >+ >+ if (spec->kctl_alloc) { >+ for (i = 0; i < spec->num_kctl_used; i++) >+ kfree(spec->kctl_alloc[i].name); >+ kfree(spec->kctl_alloc); >+ } >+ >+ kfree(codec->spec); >+} >+ >+static int via_init(struct hda_codec *codec) >+{ >+ struct via_spec *spec = codec->spec; >+ snd_hda_sequence_write(codec, spec->init_verbs); >+ /* Lydia Add for EAPD enable */ >+ if (!spec->dig_in_nid) { /* No Digital In connection */ >+ if (IS_VT1708_VENDORID(codec->vendor_id)) { >+ snd_hda_codec_write(codec, VT1708_DIGIN_PIN, 0, >+ AC_VERB_SET_PIN_WIDGET_CONTROL, >+ PIN_OUT); >+ snd_hda_codec_write(codec, VT1708_DIGIN_PIN, 0, >+ AC_VERB_SET_EAPD_BTLENABLE, 0x02); >+ } else if (IS_VT1709_10CH_VENDORID(codec->vendor_id) || >+ IS_VT1709_6CH_VENDORID(codec->vendor_id)) { >+ snd_hda_codec_write(codec, VT1709_DIGIN_PIN, 0, >+ AC_VERB_SET_PIN_WIDGET_CONTROL, >+ PIN_OUT); >+ snd_hda_codec_write(codec, VT1709_DIGIN_PIN, 0, >+ AC_VERB_SET_EAPD_BTLENABLE, 0x02); >+ } else if (IS_VT1708B_8CH_VENDORID(codec->vendor_id) || >+ IS_VT1708B_4CH_VENDORID(codec->vendor_id)) { >+ snd_hda_codec_write(codec, VT1708B_DIGIN_PIN, 0, >+ AC_VERB_SET_PIN_WIDGET_CONTROL, >+ PIN_OUT); >+ snd_hda_codec_write(codec, VT1708B_DIGIN_PIN, 0, >+ AC_VERB_SET_EAPD_BTLENABLE, 0x02); >+ } >+ } else /* enable SPDIF-input pin */ >+ snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0, >+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); >+ >+ return 0; >+} >+ >+#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 struct hda_codec_ops via_patch_ops = { >+ .build_controls = via_build_controls, >+ .build_pcms = via_build_pcms, >+ .init = via_init, >+ .free = via_free, >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+ .check_power_status = via_check_power_status, >+#endif >+}; >+ >+/* fill in the dac_nids table from the parsed pin configuration */ >+static int vt1708_auto_fill_dac_nids(struct via_spec *spec, >+ const struct auto_pin_cfg *cfg) >+{ >+ int i; >+ hda_nid_t nid; >+ >+ spec->multiout.num_dacs = cfg->line_outs; >+ >+ spec->multiout.dac_nids = spec->private_dac_nids; >+ >+ for(i = 0; i < 4; i++) { >+ nid = cfg->line_out_pins[i]; >+ if (nid) { >+ /* config dac list */ >+ switch (i) { >+ case AUTO_SEQ_FRONT: >+ spec->multiout.dac_nids[i] = 0x10; >+ break; >+ case AUTO_SEQ_CENLFE: >+ spec->multiout.dac_nids[i] = 0x12; >+ break; >+ case AUTO_SEQ_SURROUND: >+ spec->multiout.dac_nids[i] = 0x13; >+ break; >+ case AUTO_SEQ_SIDE: >+ spec->multiout.dac_nids[i] = 0x11; >+ break; >+ } >+ } >+ } >+ >+ return 0; >+} >+ >+/* add playback controls from the parsed DAC table */ >+static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec, >+ const struct auto_pin_cfg *cfg) >+{ >+ char name[32]; >+ static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; >+ hda_nid_t nid, nid_vol = 0; >+ int i, err; >+ >+ for (i = 0; i <= AUTO_SEQ_SIDE; i++) { >+ nid = cfg->line_out_pins[i]; >+ >+ if (!nid) >+ continue; >+ >+ if (i != AUTO_SEQ_FRONT) >+ nid_vol = 0x1b - i + 1; >+ >+ if (i == AUTO_SEQ_CENLFE) { >+ /* Center/LFE */ >+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, >+ "Center Playback Volume", >+ HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, >+ "LFE Playback Volume", >+ HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, >+ "Center Playback Switch", >+ HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, >+ "LFE Playback Switch", >+ HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ } else if (i == AUTO_SEQ_FRONT){ >+ /* add control to mixer index 0 */ >+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, >+ "Master Front Playback Volume", >+ HDA_COMPOSE_AMP_VAL(0x17, 3, 0, >+ HDA_INPUT)); >+ if (err < 0) >+ return err; >+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, >+ "Master Front Playback Switch", >+ HDA_COMPOSE_AMP_VAL(0x17, 3, 0, >+ HDA_INPUT)); >+ if (err < 0) >+ return err; >+ >+ /* add control to PW3 */ >+ sprintf(name, "%s Playback Volume", chname[i]); >+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, >+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ sprintf(name, "%s Playback Switch", chname[i]); >+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, >+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ } else { >+ sprintf(name, "%s Playback Volume", chname[i]); >+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, >+ HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ sprintf(name, "%s Playback Switch", chname[i]); >+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, >+ HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ } >+ } >+ >+ return 0; >+} >+ >+static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) >+{ >+ int err; >+ >+ if (!pin) >+ return 0; >+ >+ spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */ >+ >+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, >+ "Headphone Playback Volume", >+ HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, >+ "Headphone Playback Switch", >+ HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ >+ return 0; >+} >+ >+/* create playback/capture controls for input pins */ >+static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec, >+ const struct auto_pin_cfg *cfg) >+{ >+ static char *labels[] = { >+ "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL >+ }; >+ struct hda_input_mux *imux = &spec->private_imux; >+ int i, err, idx = 0; >+ >+ /* for internal loopback recording select */ >+ imux->items[imux->num_items].label = "Stereo Mixer"; >+ imux->items[imux->num_items].index = idx; >+ imux->num_items++; >+ >+ for (i = 0; i < AUTO_PIN_LAST; i++) { >+ if (!cfg->input_pins[i]) >+ continue; >+ >+ switch (cfg->input_pins[i]) { >+ case 0x1d: /* Mic */ >+ idx = 2; >+ break; >+ >+ case 0x1e: /* Line In */ >+ idx = 3; >+ break; >+ >+ case 0x21: /* Front Mic */ >+ idx = 4; >+ break; >+ >+ case 0x24: /* CD */ >+ idx = 1; >+ break; >+ } >+ err = via_new_analog_input(spec, cfg->input_pins[i], labels[i], >+ idx, 0x17); >+ if (err < 0) >+ return err; >+ imux->items[imux->num_items].label = labels[i]; >+ imux->items[imux->num_items].index = idx; >+ imux->num_items++; >+ } >+ return 0; >+} >+ >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+static struct hda_amp_list vt1708_loopbacks[] = { >+ { 0x17, HDA_INPUT, 1 }, >+ { 0x17, HDA_INPUT, 2 }, >+ { 0x17, HDA_INPUT, 3 }, >+ { 0x17, HDA_INPUT, 4 }, >+ { } /* end */ >+}; >+#endif >+ >+static int vt1708_parse_auto_config(struct hda_codec *codec) >+{ >+ struct via_spec *spec = codec->spec; >+ int err; >+ >+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); >+ if (err < 0) >+ return err; >+ err = vt1708_auto_fill_dac_nids(spec, &spec->autocfg); >+ if (err < 0) >+ return err; >+ if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) >+ return 0; /* can't find valid BIOS pin config */ >+ >+ err = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg); >+ if (err < 0) >+ return err; >+ err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); >+ if (err < 0) >+ return err; >+ err = vt1708_auto_create_analog_input_ctls(spec, &spec->autocfg); >+ if (err < 0) >+ return err; >+ >+ spec->multiout.max_channels = spec->multiout.num_dacs * 2; >+ >+ if (spec->autocfg.dig_out_pin) >+ spec->multiout.dig_out_nid = VT1708_DIGOUT_NID; >+ if (spec->autocfg.dig_in_pin) >+ spec->dig_in_nid = VT1708_DIGIN_NID; >+ >+ if (spec->kctl_alloc) >+ spec->mixers[spec->num_mixers++] = spec->kctl_alloc; >+ >+ spec->init_verbs = vt1708_volume_init_verbs; >+ >+ spec->input_mux = &spec->private_imux; >+ >+ return 1; >+} >+ >+/* init callback for auto-configuration model -- overriding the default init */ >+static int via_auto_init(struct hda_codec *codec) >+{ >+ via_init(codec); >+ via_auto_init_multi_out(codec); >+ via_auto_init_hp_out(codec); >+ via_auto_init_analog_input(codec); >+ return 0; >+} >+ >+static int patch_vt1708(struct hda_codec *codec) >+{ >+ struct via_spec *spec; >+ int err; >+ >+ /* create a codec specific record */ >+ spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); >+ if (spec == NULL) >+ return -ENOMEM; >+ >+ codec->spec = spec; >+ >+ /* automatic parse from the BIOS config */ >+ err = vt1708_parse_auto_config(codec); >+ if (err < 0) { >+ via_free(codec); >+ return err; >+ } else if (!err) { >+ printk(KERN_INFO "hda_codec: Cannot set up configuration " >+ "from BIOS. Using genenic mode...\n"); >+ } >+ >+ >+ spec->stream_name_analog = "VT1708 Analog"; >+ spec->stream_analog_playback = &vt1708_pcm_analog_playback; >+ /* disable 32bit format on VT1708 */ >+ if (codec->vendor_id == 0x11061708) >+ spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback; >+ spec->stream_analog_capture = &vt1708_pcm_analog_capture; >+ >+ spec->stream_name_digital = "VT1708 Digital"; >+ spec->stream_digital_playback = &vt1708_pcm_digital_playback; >+ spec->stream_digital_capture = &vt1708_pcm_digital_capture; >+ >+ >+ if (!spec->adc_nids && spec->input_mux) { >+ spec->adc_nids = vt1708_adc_nids; >+ spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids); >+ spec->mixers[spec->num_mixers] = vt1708_capture_mixer; >+ spec->num_mixers++; >+ } >+ >+ codec->patch_ops = via_patch_ops; >+ >+ codec->patch_ops.init = via_auto_init; >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+ spec->loopback.amplist = vt1708_loopbacks; >+#endif >+ >+ return 0; >+} >+ >+/* capture mixer elements */ >+static struct snd_kcontrol_new vt1709_capture_mixer[] = { >+ HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ /* The multiple "Capture Source" controls confuse alsamixer >+ * So call somewhat different.. >+ */ >+ /* .name = "Capture Source", */ >+ .name = "Input Source", >+ .count = 1, >+ .info = via_mux_enum_info, >+ .get = via_mux_enum_get, >+ .put = via_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+ >+/* >+ * generic initialization of ADC, input mixers and output mixers >+ */ >+static struct hda_verb vt1709_10ch_volume_init_verbs[] = { >+ /* >+ * Unmute ADC0-2 and set the default input to mic-in >+ */ >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ >+ >+ /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback >+ * mixer widget >+ */ >+ /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, >+ >+ /* >+ * Set up output selector (0x1a, 0x1b, 0x29) >+ */ >+ /* set vol=0 to output mixers */ >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ >+ /* >+ * Unmute PW3 and PW4 >+ */ >+ {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ >+ /* Set input of PW4 as AOW4 */ >+ {0x20, AC_VERB_SET_CONNECT_SEL, 0x1}, >+ /* PW9 Output enable */ >+ {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, >+ { } >+}; >+ >+static struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = { >+ .substreams = 1, >+ .channels_min = 2, >+ .channels_max = 10, >+ .nid = 0x10, /* NID to query formats and rates */ >+ .ops = { >+ .open = via_playback_pcm_open, >+ .prepare = via_playback_pcm_prepare, >+ .cleanup = via_playback_pcm_cleanup >+ }, >+}; >+ >+static struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = { >+ .substreams = 1, >+ .channels_min = 2, >+ .channels_max = 6, >+ .nid = 0x10, /* NID to query formats and rates */ >+ .ops = { >+ .open = via_playback_pcm_open, >+ .prepare = via_playback_pcm_prepare, >+ .cleanup = via_playback_pcm_cleanup >+ }, >+}; >+ >+static struct hda_pcm_stream vt1709_pcm_analog_capture = { >+ .substreams = 2, >+ .channels_min = 2, >+ .channels_max = 2, >+ .nid = 0x14, /* NID to query formats and rates */ >+ .ops = { >+ .prepare = via_capture_pcm_prepare, >+ .cleanup = via_capture_pcm_cleanup >+ }, >+}; >+ >+static struct hda_pcm_stream vt1709_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 >+ }, >+}; >+ >+static struct hda_pcm_stream vt1709_pcm_digital_capture = { >+ .substreams = 1, >+ .channels_min = 2, >+ .channels_max = 2, >+}; >+ >+static int vt1709_auto_fill_dac_nids(struct via_spec *spec, >+ const struct auto_pin_cfg *cfg) >+{ >+ int i; >+ hda_nid_t nid; >+ >+ if (cfg->line_outs == 4) /* 10 channels */ >+ spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */ >+ else if (cfg->line_outs == 3) /* 6 channels */ >+ spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */ >+ >+ spec->multiout.dac_nids = spec->private_dac_nids; >+ >+ if (cfg->line_outs == 4) { /* 10 channels */ >+ for (i = 0; i < cfg->line_outs; i++) { >+ nid = cfg->line_out_pins[i]; >+ if (nid) { >+ /* config dac list */ >+ switch (i) { >+ case AUTO_SEQ_FRONT: >+ /* AOW0 */ >+ spec->multiout.dac_nids[i] = 0x10; >+ break; >+ case AUTO_SEQ_CENLFE: >+ /* AOW2 */ >+ spec->multiout.dac_nids[i] = 0x12; >+ break; >+ case AUTO_SEQ_SURROUND: >+ /* AOW3 */ >+ spec->multiout.dac_nids[i] = 0x27; >+ break; >+ case AUTO_SEQ_SIDE: >+ /* AOW1 */ >+ spec->multiout.dac_nids[i] = 0x11; >+ break; >+ default: >+ break; >+ } >+ } >+ } >+ spec->multiout.dac_nids[cfg->line_outs] = 0x28; /* AOW4 */ >+ >+ } else if (cfg->line_outs == 3) { /* 6 channels */ >+ for(i = 0; i < cfg->line_outs; i++) { >+ nid = cfg->line_out_pins[i]; >+ if (nid) { >+ /* config dac list */ >+ switch(i) { >+ case AUTO_SEQ_FRONT: >+ /* AOW0 */ >+ spec->multiout.dac_nids[i] = 0x10; >+ break; >+ case AUTO_SEQ_CENLFE: >+ /* AOW2 */ >+ spec->multiout.dac_nids[i] = 0x12; >+ break; >+ case AUTO_SEQ_SURROUND: >+ /* AOW1 */ >+ spec->multiout.dac_nids[i] = 0x11; >+ break; >+ default: >+ break; >+ } >+ } >+ } >+ } >+ >+ return 0; >+} >+ >+/* add playback controls from the parsed DAC table */ >+static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec, >+ const struct auto_pin_cfg *cfg) >+{ >+ char name[32]; >+ static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; >+ hda_nid_t nid = 0; >+ int i, err; >+ >+ for (i = 0; i <= AUTO_SEQ_SIDE; i++) { >+ nid = cfg->line_out_pins[i]; >+ >+ if (!nid) >+ continue; >+ >+ if (i == AUTO_SEQ_CENLFE) { >+ /* Center/LFE */ >+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, >+ "Center Playback Volume", >+ HDA_COMPOSE_AMP_VAL(0x1b, 1, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, >+ "LFE Playback Volume", >+ HDA_COMPOSE_AMP_VAL(0x1b, 2, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, >+ "Center Playback Switch", >+ HDA_COMPOSE_AMP_VAL(0x1b, 1, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, >+ "LFE Playback Switch", >+ HDA_COMPOSE_AMP_VAL(0x1b, 2, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ } else if (i == AUTO_SEQ_FRONT){ >+ /* add control to mixer index 0 */ >+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, >+ "Master Front Playback Volume", >+ HDA_COMPOSE_AMP_VAL(0x18, 3, 0, >+ HDA_INPUT)); >+ if (err < 0) >+ return err; >+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, >+ "Master Front Playback Switch", >+ HDA_COMPOSE_AMP_VAL(0x18, 3, 0, >+ HDA_INPUT)); >+ if (err < 0) >+ return err; >+ >+ /* add control to PW3 */ >+ sprintf(name, "%s Playback Volume", chname[i]); >+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, >+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ sprintf(name, "%s Playback Switch", chname[i]); >+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, >+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ } else if (i == AUTO_SEQ_SURROUND) { >+ sprintf(name, "%s Playback Volume", chname[i]); >+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, >+ HDA_COMPOSE_AMP_VAL(0x29, 3, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ sprintf(name, "%s Playback Switch", chname[i]); >+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, >+ HDA_COMPOSE_AMP_VAL(0x29, 3, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ } else if (i == AUTO_SEQ_SIDE) { >+ sprintf(name, "%s Playback Volume", chname[i]); >+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, >+ HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ sprintf(name, "%s Playback Switch", chname[i]); >+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, >+ HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ } >+ } >+ >+ return 0; >+} >+ >+static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) >+{ >+ int err; >+ >+ if (!pin) >+ return 0; >+ >+ if (spec->multiout.num_dacs == 5) /* 10 channels */ >+ spec->multiout.hp_nid = VT1709_HP_DAC_NID; >+ else if (spec->multiout.num_dacs == 3) /* 6 channels */ >+ spec->multiout.hp_nid = 0; >+ >+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, >+ "Headphone Playback Volume", >+ HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, >+ "Headphone Playback Switch", >+ HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ >+ return 0; >+} >+ >+/* create playback/capture controls for input pins */ >+static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec, >+ const struct auto_pin_cfg *cfg) >+{ >+ static char *labels[] = { >+ "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL >+ }; >+ struct hda_input_mux *imux = &spec->private_imux; >+ int i, err, idx = 0; >+ >+ /* for internal loopback recording select */ >+ imux->items[imux->num_items].label = "Stereo Mixer"; >+ imux->items[imux->num_items].index = idx; >+ imux->num_items++; >+ >+ for (i = 0; i < AUTO_PIN_LAST; i++) { >+ if (!cfg->input_pins[i]) >+ continue; >+ >+ switch (cfg->input_pins[i]) { >+ case 0x1d: /* Mic */ >+ idx = 2; >+ break; >+ >+ case 0x1e: /* Line In */ >+ idx = 3; >+ break; >+ >+ case 0x21: /* Front Mic */ >+ idx = 4; >+ break; >+ >+ case 0x23: /* CD */ >+ idx = 1; >+ break; >+ } >+ err = via_new_analog_input(spec, cfg->input_pins[i], labels[i], >+ idx, 0x18); >+ if (err < 0) >+ return err; >+ imux->items[imux->num_items].label = labels[i]; >+ imux->items[imux->num_items].index = idx; >+ imux->num_items++; >+ } >+ return 0; >+} >+ >+static int vt1709_parse_auto_config(struct hda_codec *codec) >+{ >+ struct via_spec *spec = codec->spec; >+ int err; >+ >+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); >+ if (err < 0) >+ return err; >+ err = vt1709_auto_fill_dac_nids(spec, &spec->autocfg); >+ if (err < 0) >+ return err; >+ if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) >+ return 0; /* can't find valid BIOS pin config */ >+ >+ err = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg); >+ if (err < 0) >+ return err; >+ err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); >+ if (err < 0) >+ return err; >+ err = vt1709_auto_create_analog_input_ctls(spec, &spec->autocfg); >+ if (err < 0) >+ return err; >+ >+ spec->multiout.max_channels = spec->multiout.num_dacs * 2; >+ >+ if (spec->autocfg.dig_out_pin) >+ spec->multiout.dig_out_nid = VT1709_DIGOUT_NID; >+ if (spec->autocfg.dig_in_pin) >+ spec->dig_in_nid = VT1709_DIGIN_NID; >+ >+ if (spec->kctl_alloc) >+ spec->mixers[spec->num_mixers++] = spec->kctl_alloc; >+ >+ spec->input_mux = &spec->private_imux; >+ >+ return 1; >+} >+ >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+static struct hda_amp_list vt1709_loopbacks[] = { >+ { 0x18, HDA_INPUT, 1 }, >+ { 0x18, HDA_INPUT, 2 }, >+ { 0x18, HDA_INPUT, 3 }, >+ { 0x18, HDA_INPUT, 4 }, >+ { } /* end */ >+}; >+#endif >+ >+static int patch_vt1709_10ch(struct hda_codec *codec) >+{ >+ struct via_spec *spec; >+ int err; >+ >+ /* create a codec specific record */ >+ spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); >+ if (spec == NULL) >+ return -ENOMEM; >+ >+ codec->spec = spec; >+ >+ err = vt1709_parse_auto_config(codec); >+ if (err < 0) { >+ via_free(codec); >+ return err; >+ } else if (!err) { >+ printk(KERN_INFO "hda_codec: Cannot set up configuration. " >+ "Using genenic mode...\n"); >+ } >+ >+ spec->init_verbs = vt1709_10ch_volume_init_verbs; >+ >+ spec->stream_name_analog = "VT1709 Analog"; >+ spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback; >+ spec->stream_analog_capture = &vt1709_pcm_analog_capture; >+ >+ spec->stream_name_digital = "VT1709 Digital"; >+ spec->stream_digital_playback = &vt1709_pcm_digital_playback; >+ spec->stream_digital_capture = &vt1709_pcm_digital_capture; >+ >+ >+ if (!spec->adc_nids && spec->input_mux) { >+ spec->adc_nids = vt1709_adc_nids; >+ spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids); >+ spec->mixers[spec->num_mixers] = vt1709_capture_mixer; >+ spec->num_mixers++; >+ } >+ >+ codec->patch_ops = via_patch_ops; >+ >+ codec->patch_ops.init = via_auto_init; >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+ spec->loopback.amplist = vt1709_loopbacks; >+#endif >+ >+ return 0; >+} >+/* >+ * generic initialization of ADC, input mixers and output mixers >+ */ >+static struct hda_verb vt1709_6ch_volume_init_verbs[] = { >+ /* >+ * Unmute ADC0-2 and set the default input to mic-in >+ */ >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ >+ >+ /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback >+ * mixer widget >+ */ >+ /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, >+ >+ /* >+ * Set up output selector (0x1a, 0x1b, 0x29) >+ */ >+ /* set vol=0 to output mixers */ >+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ >+ /* >+ * Unmute PW3 and PW4 >+ */ >+ {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ >+ /* Set input of PW4 as MW0 */ >+ {0x20, AC_VERB_SET_CONNECT_SEL, 0}, >+ /* PW9 Output enable */ >+ {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, >+ { } >+}; >+ >+static int patch_vt1709_6ch(struct hda_codec *codec) >+{ >+ struct via_spec *spec; >+ int err; >+ >+ /* create a codec specific record */ >+ spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); >+ if (spec == NULL) >+ return -ENOMEM; >+ >+ codec->spec = spec; >+ >+ err = vt1709_parse_auto_config(codec); >+ if (err < 0) { >+ via_free(codec); >+ return err; >+ } else if (!err) { >+ printk(KERN_INFO "hda_codec: Cannot set up configuration. " >+ "Using genenic mode...\n"); >+ } >+ >+ spec->init_verbs = vt1709_6ch_volume_init_verbs; >+ >+ spec->stream_name_analog = "VT1709 Analog"; >+ spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback; >+ spec->stream_analog_capture = &vt1709_pcm_analog_capture; >+ >+ spec->stream_name_digital = "VT1709 Digital"; >+ spec->stream_digital_playback = &vt1709_pcm_digital_playback; >+ spec->stream_digital_capture = &vt1709_pcm_digital_capture; >+ >+ >+ if (!spec->adc_nids && spec->input_mux) { >+ spec->adc_nids = vt1709_adc_nids; >+ spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids); >+ spec->mixers[spec->num_mixers] = vt1709_capture_mixer; >+ spec->num_mixers++; >+ } >+ >+ codec->patch_ops = via_patch_ops; >+ >+ codec->patch_ops.init = via_auto_init; >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+ spec->loopback.amplist = vt1709_loopbacks; >+#endif >+ return 0; >+} >+ >+/* capture mixer elements */ >+static struct snd_kcontrol_new vt1708B_capture_mixer[] = { >+ HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), >+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), >+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), >+ { >+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, >+ /* The multiple "Capture Source" controls confuse alsamixer >+ * So call somewhat different.. >+ */ >+ /* .name = "Capture Source", */ >+ .name = "Input Source", >+ .count = 1, >+ .info = via_mux_enum_info, >+ .get = via_mux_enum_get, >+ .put = via_mux_enum_put, >+ }, >+ { } /* end */ >+}; >+/* >+ * generic initialization of ADC, input mixers and output mixers >+ */ >+static struct hda_verb vt1708B_8ch_volume_init_verbs[] = { >+ /* >+ * Unmute ADC0-1 and set the default input to mic-in >+ */ >+ {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ >+ >+ /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback >+ * mixer widget >+ */ >+ /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, >+ >+ /* >+ * Set up output mixers >+ */ >+ /* set vol=0 to output mixers */ >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ >+ /* Setup default input to PW4 */ >+ {0x1d, AC_VERB_SET_CONNECT_SEL, 0x1}, >+ /* PW9 Output enable */ >+ {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, >+ /* PW10 Input enable */ >+ {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, >+ { } >+}; >+ >+static struct hda_verb vt1708B_4ch_volume_init_verbs[] = { >+ /* >+ * Unmute ADC0-1 and set the default input to mic-in >+ */ >+ {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ >+ >+ /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback >+ * mixer widget >+ */ >+ /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, >+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, >+ >+ /* >+ * Set up output mixers >+ */ >+ /* set vol=0 to output mixers */ >+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, >+ >+ /* Setup default input of PW4 to MW0 */ >+ {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, >+ /* PW9 Output enable */ >+ {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, >+ /* PW10 Input enable */ >+ {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, >+ { } >+}; >+ >+static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = { >+ .substreams = 1, >+ .channels_min = 2, >+ .channels_max = 8, >+ .nid = 0x10, /* NID to query formats and rates */ >+ .ops = { >+ .open = via_playback_pcm_open, >+ .prepare = via_playback_pcm_prepare, >+ .cleanup = via_playback_pcm_cleanup >+ }, >+}; >+ >+static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = { >+ .substreams = 1, >+ .channels_min = 2, >+ .channels_max = 4, >+ .nid = 0x10, /* NID to query formats and rates */ >+ .ops = { >+ .open = via_playback_pcm_open, >+ .prepare = via_playback_pcm_prepare, >+ .cleanup = via_playback_pcm_cleanup >+ }, >+}; >+ >+static struct hda_pcm_stream vt1708B_pcm_analog_capture = { >+ .substreams = 2, >+ .channels_min = 2, >+ .channels_max = 2, >+ .nid = 0x13, /* NID to query formats and rates */ >+ .ops = { >+ .prepare = via_capture_pcm_prepare, >+ .cleanup = via_capture_pcm_cleanup >+ }, >+}; >+ >+static struct hda_pcm_stream vt1708B_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 >+ }, >+}; >+ >+static struct hda_pcm_stream vt1708B_pcm_digital_capture = { >+ .substreams = 1, >+ .channels_min = 2, >+ .channels_max = 2, >+}; >+ >+/* fill in the dac_nids table from the parsed pin configuration */ >+static int vt1708B_auto_fill_dac_nids(struct via_spec *spec, >+ const struct auto_pin_cfg *cfg) >+{ >+ int i; >+ hda_nid_t nid; >+ >+ spec->multiout.num_dacs = cfg->line_outs; >+ >+ spec->multiout.dac_nids = spec->private_dac_nids; >+ >+ for (i = 0; i < 4; i++) { >+ nid = cfg->line_out_pins[i]; >+ if (nid) { >+ /* config dac list */ >+ switch (i) { >+ case AUTO_SEQ_FRONT: >+ spec->multiout.dac_nids[i] = 0x10; >+ break; >+ case AUTO_SEQ_CENLFE: >+ spec->multiout.dac_nids[i] = 0x24; >+ break; >+ case AUTO_SEQ_SURROUND: >+ spec->multiout.dac_nids[i] = 0x25; >+ break; >+ case AUTO_SEQ_SIDE: >+ spec->multiout.dac_nids[i] = 0x11; >+ break; >+ } >+ } >+ } >+ >+ return 0; >+} >+ >+/* add playback controls from the parsed DAC table */ >+static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec, >+ const struct auto_pin_cfg *cfg) >+{ >+ char name[32]; >+ static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; >+ hda_nid_t nid_vols[] = {0x16, 0x27, 0x26, 0x18}; >+ hda_nid_t nid, nid_vol = 0; >+ int i, err; >+ >+ for (i = 0; i <= AUTO_SEQ_SIDE; i++) { >+ nid = cfg->line_out_pins[i]; >+ >+ if (!nid) >+ continue; >+ >+ nid_vol = nid_vols[i]; >+ >+ if (i == AUTO_SEQ_CENLFE) { >+ /* Center/LFE */ >+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, >+ "Center Playback Volume", >+ HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, >+ "LFE Playback Volume", >+ HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, >+ "Center Playback Switch", >+ HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, >+ "LFE Playback Switch", >+ HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ } else if (i == AUTO_SEQ_FRONT) { >+ /* add control to mixer index 0 */ >+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, >+ "Master Front Playback Volume", >+ HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, >+ HDA_INPUT)); >+ if (err < 0) >+ return err; >+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, >+ "Master Front Playback Switch", >+ HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, >+ HDA_INPUT)); >+ if (err < 0) >+ return err; >+ >+ /* add control to PW3 */ >+ sprintf(name, "%s Playback Volume", chname[i]); >+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, >+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ sprintf(name, "%s Playback Switch", chname[i]); >+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, >+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ } else { >+ sprintf(name, "%s Playback Volume", chname[i]); >+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, >+ HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ sprintf(name, "%s Playback Switch", chname[i]); >+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, >+ HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, >+ HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ } >+ } >+ >+ return 0; >+} >+ >+static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) >+{ >+ int err; >+ >+ if (!pin) >+ return 0; >+ >+ spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */ >+ >+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, >+ "Headphone Playback Volume", >+ HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, >+ "Headphone Playback Switch", >+ HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); >+ if (err < 0) >+ return err; >+ >+ return 0; >+} >+ >+/* create playback/capture controls for input pins */ >+static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec, >+ const struct auto_pin_cfg *cfg) >+{ >+ static char *labels[] = { >+ "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL >+ }; >+ struct hda_input_mux *imux = &spec->private_imux; >+ int i, err, idx = 0; >+ >+ /* for internal loopback recording select */ >+ imux->items[imux->num_items].label = "Stereo Mixer"; >+ imux->items[imux->num_items].index = idx; >+ imux->num_items++; >+ >+ for (i = 0; i < AUTO_PIN_LAST; i++) { >+ if (!cfg->input_pins[i]) >+ continue; >+ >+ switch (cfg->input_pins[i]) { >+ case 0x1a: /* Mic */ >+ idx = 2; >+ break; >+ >+ case 0x1b: /* Line In */ >+ idx = 3; >+ break; >+ >+ case 0x1e: /* Front Mic */ >+ idx = 4; >+ break; >+ >+ case 0x1f: /* CD */ >+ idx = 1; >+ break; >+ } >+ err = via_new_analog_input(spec, cfg->input_pins[i], labels[i], >+ idx, 0x16); >+ if (err < 0) >+ return err; >+ imux->items[imux->num_items].label = labels[i]; >+ imux->items[imux->num_items].index = idx; >+ imux->num_items++; >+ } >+ return 0; >+} >+ >+static int vt1708B_parse_auto_config(struct hda_codec *codec) >+{ >+ struct via_spec *spec = codec->spec; >+ int err; >+ >+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); >+ if (err < 0) >+ return err; >+ err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg); >+ if (err < 0) >+ return err; >+ if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) >+ return 0; /* can't find valid BIOS pin config */ >+ >+ err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg); >+ if (err < 0) >+ return err; >+ err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); >+ if (err < 0) >+ return err; >+ err = vt1708B_auto_create_analog_input_ctls(spec, &spec->autocfg); >+ if (err < 0) >+ return err; >+ >+ spec->multiout.max_channels = spec->multiout.num_dacs * 2; >+ >+ if (spec->autocfg.dig_out_pin) >+ spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID; >+ if (spec->autocfg.dig_in_pin) >+ spec->dig_in_nid = VT1708B_DIGIN_NID; >+ >+ if (spec->kctl_alloc) >+ spec->mixers[spec->num_mixers++] = spec->kctl_alloc; >+ >+ spec->input_mux = &spec->private_imux; >+ >+ return 1; >+} >+ >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+static struct hda_amp_list vt1708B_loopbacks[] = { >+ { 0x16, HDA_INPUT, 1 }, >+ { 0x16, HDA_INPUT, 2 }, >+ { 0x16, HDA_INPUT, 3 }, >+ { 0x16, HDA_INPUT, 4 }, >+ { } /* end */ >+}; >+#endif >+ >+static int patch_vt1708B_8ch(struct hda_codec *codec) >+{ >+ struct via_spec *spec; >+ int err; >+ >+ /* create a codec specific record */ >+ spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); >+ if (spec == NULL) >+ return -ENOMEM; >+ >+ codec->spec = spec; >+ >+ /* automatic parse from the BIOS config */ >+ err = vt1708B_parse_auto_config(codec); >+ if (err < 0) { >+ via_free(codec); >+ return err; >+ } else if (!err) { >+ printk(KERN_INFO "hda_codec: Cannot set up configuration " >+ "from BIOS. Using genenic mode...\n"); >+ } >+ >+ spec->init_verbs = vt1708B_8ch_volume_init_verbs; >+ >+ spec->stream_name_analog = "VT1708B Analog"; >+ spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback; >+ spec->stream_analog_capture = &vt1708B_pcm_analog_capture; >+ >+ spec->stream_name_digital = "VT1708B Digital"; >+ spec->stream_digital_playback = &vt1708B_pcm_digital_playback; >+ spec->stream_digital_capture = &vt1708B_pcm_digital_capture; >+ >+ if (!spec->adc_nids && spec->input_mux) { >+ spec->adc_nids = vt1708B_adc_nids; >+ spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids); >+ spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; >+ spec->num_mixers++; >+ } >+ >+ codec->patch_ops = via_patch_ops; >+ >+ codec->patch_ops.init = via_auto_init; >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+ spec->loopback.amplist = vt1708B_loopbacks; >+#endif >+ >+ return 0; >+} >+ >+static int patch_vt1708B_4ch(struct hda_codec *codec) >+{ >+ struct via_spec *spec; >+ int err; >+ >+ /* create a codec specific record */ >+ spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); >+ if (spec == NULL) >+ return -ENOMEM; >+ >+ codec->spec = spec; >+ >+ /* automatic parse from the BIOS config */ >+ err = vt1708B_parse_auto_config(codec); >+ if (err < 0) { >+ via_free(codec); >+ return err; >+ } else if (!err) { >+ printk(KERN_INFO "hda_codec: Cannot set up configuration " >+ "from BIOS. Using genenic mode...\n"); >+ } >+ >+ spec->init_verbs = vt1708B_4ch_volume_init_verbs; >+ >+ spec->stream_name_analog = "VT1708B Analog"; >+ spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback; >+ spec->stream_analog_capture = &vt1708B_pcm_analog_capture; >+ >+ spec->stream_name_digital = "VT1708B Digital"; >+ spec->stream_digital_playback = &vt1708B_pcm_digital_playback; >+ spec->stream_digital_capture = &vt1708B_pcm_digital_capture; >+ >+ if (!spec->adc_nids && spec->input_mux) { >+ spec->adc_nids = vt1708B_adc_nids; >+ spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids); >+ spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; >+ spec->num_mixers++; >+ } >+ >+ codec->patch_ops = via_patch_ops; >+ >+ codec->patch_ops.init = via_auto_init; >+#ifdef CONFIG_SND_HDA_POWER_SAVE >+ spec->loopback.amplist = vt1708B_loopbacks; >+#endif >+ >+ return 0; >+} >+ >+/* >+ * patch entries >+ */ >+struct hda_codec_preset snd_hda_preset_via[] = { >+ { .id = 0x11061708, .name = "VIA VT1708", .patch = patch_vt1708}, >+ { .id = 0x11061709, .name = "VIA VT1708", .patch = patch_vt1708}, >+ { .id = 0x1106170A, .name = "VIA VT1708", .patch = patch_vt1708}, >+ { .id = 0x1106170B, .name = "VIA VT1708", .patch = patch_vt1708}, >+ { .id = 0x1106E710, .name = "VIA VT1709 10-Ch", >+ .patch = patch_vt1709_10ch}, >+ { .id = 0x1106E711, .name = "VIA VT1709 10-Ch", >+ .patch = patch_vt1709_10ch}, >+ { .id = 0x1106E712, .name = "VIA VT1709 10-Ch", >+ .patch = patch_vt1709_10ch}, >+ { .id = 0x1106E713, .name = "VIA VT1709 10-Ch", >+ .patch = patch_vt1709_10ch}, >+ { .id = 0x1106E714, .name = "VIA VT1709 6-Ch", >+ .patch = patch_vt1709_6ch}, >+ { .id = 0x1106E715, .name = "VIA VT1709 6-Ch", >+ .patch = patch_vt1709_6ch}, >+ { .id = 0x1106E716, .name = "VIA VT1709 6-Ch", >+ .patch = patch_vt1709_6ch}, >+ { .id = 0x1106E717, .name = "VIA VT1709 6-Ch", >+ .patch = patch_vt1709_6ch}, >+ { .id = 0x1106E720, .name = "VIA VT1708B 8-Ch", >+ .patch = patch_vt1708B_8ch}, >+ { .id = 0x1106E721, .name = "VIA VT1708B 8-Ch", >+ .patch = patch_vt1708B_8ch}, >+ { .id = 0x1106E722, .name = "VIA VT1708B 8-Ch", >+ .patch = patch_vt1708B_8ch}, >+ { .id = 0x1106E723, .name = "VIA VT1708B 8-Ch", >+ .patch = patch_vt1708B_8ch}, >+ { .id = 0x1106E724, .name = "VIA VT1708B 4-Ch", >+ .patch = patch_vt1708B_4ch}, >+ { .id = 0x1106E725, .name = "VIA VT1708B 4-Ch", >+ .patch = patch_vt1708B_4ch}, >+ { .id = 0x1106E726, .name = "VIA VT1708B 4-Ch", >+ .patch = patch_vt1708B_4ch}, >+ { .id = 0x1106E727, .name = "VIA VT1708B 4-Ch", >+ .patch = patch_vt1708B_4ch}, >+ {} /* terminator */ >+}; >-- >1.5.5.1 >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 452846
: 310265