summaryrefslogtreecommitdiffstats
path: root/sound/pci
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci')
-rw-r--r--sound/pci/Kconfig13
-rw-r--r--sound/pci/hda/Makefile2
-rw-r--r--sound/pci/hda/hda_codec.c503
-rw-r--r--sound/pci/hda/hda_codec.h64
-rw-r--r--sound/pci/hda/hda_eld.c528
-rw-r--r--sound/pci/hda/hda_generic.c20
-rw-r--r--sound/pci/hda/hda_hwdep.c228
-rw-r--r--sound/pci/hda/hda_intel.c276
-rw-r--r--sound/pci/hda/hda_local.h77
-rw-r--r--sound/pci/hda/hda_patch.h2
-rw-r--r--sound/pci/hda/hda_proc.c64
-rw-r--r--sound/pci/hda/patch_analog.c56
-rw-r--r--sound/pci/hda/patch_atihdmi.c1
-rw-r--r--sound/pci/hda/patch_conexant.c11
-rw-r--r--sound/pci/hda/patch_intelhdmi.c461
-rw-r--r--sound/pci/hda/patch_realtek.c1328
-rw-r--r--sound/pci/hda/patch_sigmatel.c503
-rw-r--r--sound/pci/hda/patch_via.c73
18 files changed, 2897 insertions, 1313 deletions
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index 7003711f4fcc..157a0a6b10ae 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -501,6 +501,7 @@ config SND_HDA_INTEL
tristate "Intel HD Audio"
select SND_PCM
select SND_VMASTER
+ select SND_JACK if INPUT=y || INPUT=SND
help
Say Y here to include support for Intel "High Definition
Audio" (Azalia) motherboard devices.
@@ -573,6 +574,18 @@ config SND_HDA_CODEC_NVHDMI
Say Y here to include NVIDIA HDMI HD-audio codec support in
snd-hda-intel driver, such as NVIDIA MCP78 HDMI.
+config SND_HDA_CODEC_INTELHDMI
+ bool "Build INTEL HDMI HD-audio codec support"
+ depends on SND_HDA_INTEL
+ default y
+ help
+ Say Y here to include INTEL HDMI HD-audio codec support in
+ snd-hda-intel driver, such as Eaglelake integrated HDMI.
+
+config SND_HDA_ELD
+ def_bool y
+ depends on SND_HDA_CODEC_INTELHDMI
+
config SND_HDA_CODEC_CONEXANT
bool "Build Conexant HD-audio codec support"
depends on SND_HDA_INTEL
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index 1980c6d207e7..6daf5fd9a279 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -4,6 +4,7 @@ snd-hda-intel-y := hda_intel.o
# designed to be individual modules
snd-hda-intel-y += hda_codec.o
snd-hda-intel-$(CONFIG_PROC_FS) += hda_proc.o
+snd-hda-intel-$(CONFIG_SND_HDA_ELD) += hda_eld.o
snd-hda-intel-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
snd-hda-intel-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o
snd-hda-intel-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
@@ -16,5 +17,6 @@ snd-hda-intel-$(CONFIG_SND_HDA_CODEC_ATIHDMI) += patch_atihdmi.o
snd-hda-intel-$(CONFIG_SND_HDA_CODEC_CONEXANT) += patch_conexant.o
snd-hda-intel-$(CONFIG_SND_HDA_CODEC_VIA) += patch_via.o
snd-hda-intel-$(CONFIG_SND_HDA_CODEC_NVHDMI) += patch_nvhdmi.o
+snd-hda-intel-$(CONFIG_SND_HDA_CODEC_INTELHDMI) += patch_intelhdmi.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
index ba1ab737b55f..5d5e8012d6a5 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -98,6 +98,9 @@ static const struct hda_codec_preset *hda_preset_tables[] = {
#ifdef CONFIG_SND_HDA_CODEC_NVHDMI
snd_hda_preset_nvhdmi,
#endif
+#ifdef CONFIG_SND_HDA_CODEC_INTELHDMI
+ snd_hda_preset_intelhdmi,
+#endif
NULL
};
@@ -108,6 +111,69 @@ static void hda_keep_power_on(struct hda_codec *codec);
static inline void hda_keep_power_on(struct hda_codec *codec) {}
#endif
+const char *snd_hda_get_jack_location(u32 cfg)
+{
+ static char *bases[7] = {
+ "N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom",
+ };
+ static unsigned char specials_idx[] = {
+ 0x07, 0x08,
+ 0x17, 0x18, 0x19,
+ 0x37, 0x38
+ };
+ static char *specials[] = {
+ "Rear Panel", "Drive Bar",
+ "Riser", "HDMI", "ATAPI",
+ "Mobile-In", "Mobile-Out"
+ };
+ int i;
+ cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT;
+ if ((cfg & 0x0f) < 7)
+ return bases[cfg & 0x0f];
+ for (i = 0; i < ARRAY_SIZE(specials_idx); i++) {
+ if (cfg == specials_idx[i])
+ return specials[i];
+ }
+ return "UNKNOWN";
+}
+
+const char *snd_hda_get_jack_connectivity(u32 cfg)
+{
+ static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" };
+
+ return jack_locations[(cfg >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3];
+}
+
+const char *snd_hda_get_jack_type(u32 cfg)
+{
+ static char *jack_types[16] = {
+ "Line Out", "Speaker", "HP Out", "CD",
+ "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand",
+ "Line In", "Aux", "Mic", "Telephony",
+ "SPDIF In", "Digitial In", "Reserved", "Other"
+ };
+
+ return jack_types[(cfg & AC_DEFCFG_DEVICE)
+ >> AC_DEFCFG_DEVICE_SHIFT];
+}
+
+/*
+ * Compose a 32bit command word to be sent to the HD-audio controller
+ */
+static inline unsigned int
+make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
+ unsigned int verb, unsigned int parm)
+{
+ u32 val;
+
+ val = (u32)(codec->addr & 0x0f) << 28;
+ val |= (u32)direct << 27;
+ val |= (u32)nid << 20;
+ val |= verb << 8;
+ val |= parm;
+ return val;
+}
+
/**
* snd_hda_codec_read - send a command and get the response
* @codec: the HDA codec
@@ -124,14 +190,17 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
int direct,
unsigned int verb, unsigned int parm)
{
+ struct hda_bus *bus = codec->bus;
unsigned int res;
+
+ res = make_codec_cmd(codec, nid, direct, verb, parm);
snd_hda_power_up(codec);
- mutex_lock(&codec->bus->cmd_mutex);
- if (!codec->bus->ops.command(codec, nid, direct, verb, parm))
- res = codec->bus->ops.get_response(codec);
+ mutex_lock(&bus->cmd_mutex);
+ if (!bus->ops.command(bus, res))
+ res = bus->ops.get_response(bus);
else
res = (unsigned int)-1;
- mutex_unlock(&codec->bus->cmd_mutex);
+ mutex_unlock(&bus->cmd_mutex);
snd_hda_power_down(codec);
return res;
}
@@ -151,11 +220,15 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct,
unsigned int verb, unsigned int parm)
{
+ struct hda_bus *bus = codec->bus;
+ unsigned int res;
int err;
+
+ res = make_codec_cmd(codec, nid, direct, verb, parm);
snd_hda_power_up(codec);
- mutex_lock(&codec->bus->cmd_mutex);
- err = codec->bus->ops.command(codec, nid, direct, verb, parm);
- mutex_unlock(&codec->bus->cmd_mutex);
+ mutex_lock(&bus->cmd_mutex);
+ err = bus->ops.command(bus, res);
+ mutex_unlock(&bus->cmd_mutex);
snd_hda_power_down(codec);
return err;
}
@@ -345,7 +418,7 @@ static void process_unsol_events(struct work_struct *work)
/*
* initialize unsolicited queue
*/
-static int __devinit init_unsol_queue(struct hda_bus *bus)
+static int init_unsol_queue(struct hda_bus *bus)
{
struct hda_bus_unsolicited *unsol;
@@ -394,6 +467,20 @@ static int snd_hda_bus_dev_free(struct snd_device *device)
return snd_hda_bus_free(bus);
}
+#ifdef CONFIG_SND_HDA_HWDEP
+static int snd_hda_bus_dev_register(struct snd_device *device)
+{
+ struct hda_bus *bus = device->device_data;
+ struct hda_codec *codec;
+ list_for_each_entry(codec, &bus->codec_list, list) {
+ snd_hda_hwdep_add_sysfs(codec);
+ }
+ return 0;
+}
+#else
+#define snd_hda_bus_dev_register NULL
+#endif
+
/**
* snd_hda_bus_new - create a HDA bus
* @card: the card entry
@@ -409,6 +496,7 @@ int __devinit snd_hda_bus_new(struct snd_card *card,
struct hda_bus *bus;
int err;
static struct snd_device_ops dev_ops = {
+ .dev_register = snd_hda_bus_dev_register,
.dev_free = snd_hda_bus_dev_free,
};
@@ -447,7 +535,7 @@ int __devinit snd_hda_bus_new(struct snd_card *card,
#ifdef CONFIG_SND_HDA_GENERIC
#define is_generic_config(codec) \
- (codec->bus->modelname && !strcmp(codec->bus->modelname, "generic"))
+ (codec->modelname && !strcmp(codec->modelname, "generic"))
#else
#define is_generic_config(codec) 0
#endif
@@ -455,7 +543,7 @@ int __devinit snd_hda_bus_new(struct snd_card *card,
/*
* find a matching codec preset
*/
-static const struct hda_codec_preset __devinit *
+static const struct hda_codec_preset *
find_codec_preset(struct hda_codec *codec)
{
const struct hda_codec_preset **tbl, *preset;
@@ -482,15 +570,14 @@ find_codec_preset(struct hda_codec *codec)
}
/*
- * snd_hda_get_codec_name - store the codec name
+ * get_codec_name - store the codec name
*/
-void snd_hda_get_codec_name(struct hda_codec *codec,
- char *name, int namelen)
+static int get_codec_name(struct hda_codec *codec)
{
const struct hda_vendor_id *c;
const char *vendor = NULL;
u16 vendor_id = codec->vendor_id >> 16;
- char tmp[16];
+ char tmp[16], name[32];
for (c = hda_vendor_ids; c->id; c++) {
if (c->id == vendor_id) {
@@ -503,10 +590,15 @@ void snd_hda_get_codec_name(struct hda_codec *codec,
vendor = tmp;
}
if (codec->preset && codec->preset->name)
- snprintf(name, namelen, "%s %s", vendor, codec->preset->name);
+ snprintf(name, sizeof(name), "%s %s", vendor,
+ codec->preset->name);
else
- snprintf(name, namelen, "%s ID %x", vendor,
+ snprintf(name, sizeof(name), "%s ID %x", vendor,
codec->vendor_id & 0xffff);
+ codec->name = kstrdup(name, GFP_KERNEL);
+ if (!codec->name)
+ return -ENOMEM;
+ return 0;
}
/*
@@ -571,11 +663,14 @@ static void snd_hda_codec_free(struct hda_codec *codec)
flush_scheduled_work();
#endif
list_del(&codec->list);
+ snd_array_free(&codec->mixers);
codec->bus->caddr_tbl[codec->addr] = NULL;
if (codec->patch_ops.free)
codec->patch_ops.free(codec);
free_hda_cache(&codec->amp_cache);
free_hda_cache(&codec->cmd_cache);
+ kfree(codec->name);
+ kfree(codec->modelname);
kfree(codec->wcaps);
kfree(codec);
}
@@ -617,6 +712,14 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
mutex_init(&codec->spdif_mutex);
init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
+ snd_array_init(&codec->mixers, sizeof(struct snd_kcontrol *), 32);
+ if (codec->bus->modelname) {
+ codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
+ if (!codec->modelname) {
+ snd_hda_codec_free(codec);
+ return -ENODEV;
+ }
+ }
#ifdef CONFIG_SND_HDA_POWER_SAVE
INIT_DELAYED_WORK(&codec->power_work, hda_power_work);
@@ -662,12 +765,41 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_SUBSYSTEM_ID, 0);
}
+ if (bus->modelname)
+ codec->modelname = kstrdup(bus->modelname, GFP_KERNEL);
+
+ err = snd_hda_codec_configure(codec);
+ if (err < 0) {
+ snd_hda_codec_free(codec);
+ return err;
+ }
+ snd_hda_codec_proc_new(codec);
+
+ snd_hda_create_hwdep(codec);
+
+ sprintf(component, "HDA:%08x,%08x,%08x", codec->vendor_id,
+ codec->subsystem_id, codec->revision_id);
+ snd_component_add(codec->bus->card, component);
+
+ if (codecp)
+ *codecp = codec;
+ return 0;
+}
+
+int snd_hda_codec_configure(struct hda_codec *codec)
+{
+ int err;
codec->preset = find_codec_preset(codec);
+ if (!codec->name) {
+ err = get_codec_name(codec);
+ if (err < 0)
+ return err;
+ }
/* audio codec should override the mixer name */
- if (codec->afg || !*bus->card->mixername)
- snd_hda_get_codec_name(codec, bus->card->mixername,
- sizeof(bus->card->mixername));
+ if (codec->afg || !*codec->bus->card->mixername)
+ strlcpy(codec->bus->card->mixername, codec->name,
+ sizeof(codec->bus->card->mixername));
if (is_generic_config(codec)) {
err = snd_hda_parse_generic_codec(codec);
@@ -684,25 +816,9 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
printk(KERN_ERR "hda-codec: No codec parser is available\n");
patched:
- if (err < 0) {
- snd_hda_codec_free(codec);
- return err;
- }
-
- if (codec->patch_ops.unsol_event)
- init_unsol_queue(bus);
-
- snd_hda_codec_proc_new(codec);
-#ifdef CONFIG_SND_HDA_HWDEP
- snd_hda_create_hwdep(codec);
-#endif
-
- sprintf(component, "HDA:%08x,%08x,%08x", codec->vendor_id, codec->subsystem_id, codec->revision_id);
- snd_component_add(codec->bus->card, component);
-
- if (codecp)
- *codecp = codec;
- return 0;
+ if (!err && codec->patch_ops.unsol_event)
+ err = init_unsol_queue(codec->bus);
+ return err;
}
/**
@@ -757,12 +873,12 @@ static void __devinit init_hda_cache(struct hda_cache_rec *cache,
{
memset(cache, 0, sizeof(*cache));
memset(cache->hash, 0xff, sizeof(cache->hash));
- cache->record_size = record_size;
+ snd_array_init(&cache->buf, record_size, 64);
}
static void free_hda_cache(struct hda_cache_rec *cache)
{
- kfree(cache->buffer);
+ snd_array_free(&cache->buf);
}
/* query the hash. allocate an entry if not found. */
@@ -774,35 +890,17 @@ static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache,
struct hda_cache_head *info;
while (cur != 0xffff) {
- info = (struct hda_cache_head *)(cache->buffer +
- cur * cache->record_size);
+ info = snd_array_elem(&cache->buf, cur);
if (info->key == key)
return info;
cur = info->next;
}
/* add a new hash entry */
- if (cache->num_entries >= cache->size) {
- /* reallocate the array */
- unsigned int new_size = cache->size + 64;
- void *new_buffer;
- new_buffer = kcalloc(new_size, cache->record_size, GFP_KERNEL);
- if (!new_buffer) {
- snd_printk(KERN_ERR "hda_codec: "
- "can't malloc amp_info\n");
- return NULL;
- }
- if (cache->buffer) {
- memcpy(new_buffer, cache->buffer,
- cache->size * cache->record_size);
- kfree(cache->buffer);
- }
- cache->size = new_size;
- cache->buffer = new_buffer;
- }
- cur = cache->num_entries++;
- info = (struct hda_cache_head *)(cache->buffer +
- cur * cache->record_size);
+ info = snd_array_new(&cache->buf);
+ if (!info)
+ return NULL;
+ cur = snd_array_index(&cache->buf, info);
info->key = key;
info->val = 0;
info->next = cache->hash[idx];
@@ -943,10 +1041,10 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
/* resume the all amp commands from the cache */
void snd_hda_codec_resume_amp(struct hda_codec *codec)
{
- struct hda_amp_info *buffer = codec->amp_cache.buffer;
+ struct hda_amp_info *buffer = codec->amp_cache.buf.list;
int i;
- for (i = 0; i < codec->amp_cache.size; i++, buffer++) {
+ for (i = 0; i < codec->amp_cache.buf.used; i++, buffer++) {
u32 key = buffer->head.key;
hda_nid_t nid;
unsigned int idx, dir, ch;
@@ -1098,6 +1196,57 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
return _snd_hda_find_mixer_ctl(codec, name, 0);
}
+/* Add a control element and assign to the codec */
+int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl)
+{
+ int err;
+ struct snd_kcontrol **knewp;
+
+ err = snd_ctl_add(codec->bus->card, kctl);
+ if (err < 0)
+ return err;
+ knewp = snd_array_new(&codec->mixers);
+ if (!knewp)
+ return -ENOMEM;
+ *knewp = kctl;
+ return 0;
+}
+
+/* Clear all controls assigned to the given codec */
+void snd_hda_ctls_clear(struct hda_codec *codec)
+{
+ int i;
+ struct snd_kcontrol **kctls = codec->mixers.list;
+ for (i = 0; i < codec->mixers.used; i++)
+ snd_ctl_remove(codec->bus->card, kctls[i]);
+ snd_array_free(&codec->mixers);
+}
+
+void snd_hda_codec_reset(struct hda_codec *codec)
+{
+ int i;
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ cancel_delayed_work(&codec->power_work);
+ flush_scheduled_work();
+#endif
+ snd_hda_ctls_clear(codec);
+ /* relase PCMs */
+ for (i = 0; i < codec->num_pcms; i++) {
+ if (codec->pcm_info[i].pcm)
+ snd_device_free(codec->bus->card,
+ codec->pcm_info[i].pcm);
+ }
+ if (codec->patch_ops.free)
+ codec->patch_ops.free(codec);
+ codec->spec = NULL;
+ free_hda_cache(&codec->amp_cache);
+ free_hda_cache(&codec->cmd_cache);
+ codec->num_pcms = 0;
+ codec->pcm_info = NULL;
+ codec->preset = NULL;
+}
+
/* 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)
@@ -1115,7 +1264,7 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
kctl = snd_ctl_make_virtual_master(name, tlv);
if (!kctl)
return -ENOMEM;
- err = snd_ctl_add(codec->bus->card, kctl);
+ err = snd_hda_ctl_add(codec, kctl);
if (err < 0)
return err;
@@ -1577,9 +1726,11 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
}
for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) {
kctl = snd_ctl_new1(dig_mix, codec);
+ if (!kctl)
+ return -ENOMEM;
kctl->id.index = idx;
kctl->private_value = nid;
- err = snd_ctl_add(codec->bus->card, kctl);
+ err = snd_hda_ctl_add(codec, kctl);
if (err < 0)
return err;
}
@@ -1623,7 +1774,7 @@ int snd_hda_create_spdif_share_sw(struct hda_codec *codec,
if (!mout->dig_out_nid)
return 0;
/* ATTENTION: here mout is passed as private_data, instead of codec */
- return snd_ctl_add(codec->bus->card,
+ return snd_hda_ctl_add(codec,
snd_ctl_new1(&spdif_share_sw, mout));
}
@@ -1725,7 +1876,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) {
kctl = snd_ctl_new1(dig_mix, codec);
kctl->private_value = nid;
- err = snd_ctl_add(codec->bus->card, kctl);
+ err = snd_hda_ctl_add(codec, kctl);
if (err < 0)
return err;
}
@@ -1761,10 +1912,14 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
int direct, unsigned int verb, unsigned int parm)
{
+ struct hda_bus *bus = codec->bus;
+ unsigned int res;
int err;
+
+ res = make_codec_cmd(codec, nid, direct, verb, parm);
snd_hda_power_up(codec);
- mutex_lock(&codec->bus->cmd_mutex);
- err = codec->bus->ops.command(codec, nid, direct, verb, parm);
+ mutex_lock(&bus->cmd_mutex);
+ err = bus->ops.command(bus, res);
if (!err) {
struct hda_cache_head *c;
u32 key = build_cmd_cache_key(nid, verb);
@@ -1772,7 +1927,7 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
if (c)
c->val = parm;
}
- mutex_unlock(&codec->bus->cmd_mutex);
+ mutex_unlock(&bus->cmd_mutex);
snd_hda_power_down(codec);
return err;
}
@@ -1780,10 +1935,10 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
/* resume the all commands from the cache */
void snd_hda_codec_resume_cache(struct hda_codec *codec)
{
- struct hda_cache_head *buffer = codec->cmd_cache.buffer;
+ struct hda_cache_head *buffer = codec->cmd_cache.buf.list;
int i;
- for (i = 0; i < codec->cmd_cache.size; i++, buffer++) {
+ for (i = 0; i < codec->cmd_cache.buf.used; i++, buffer++) {
u32 key = buffer->key;
if (!key)
continue;
@@ -1868,6 +2023,17 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
}
}
+#ifdef CONFIG_SND_HDA_HWDEP
+/* execute additional init verbs */
+static void hda_exec_init_verbs(struct hda_codec *codec)
+{
+ if (codec->init_verbs.list)
+ snd_hda_sequence_write(codec, codec->init_verbs.list);
+}
+#else
+static inline void hda_exec_init_verbs(struct hda_codec *codec) {}
+#endif
+
#ifdef SND_HDA_NEEDS_RESUME
/*
* call suspend and power-down; used both from PM and power-save
@@ -1894,6 +2060,7 @@ static void hda_call_codec_resume(struct hda_codec *codec)
hda_set_power_state(codec,
codec->afg ? codec->afg : codec->mfg,
AC_PWRST_D0);
+ hda_exec_init_verbs(codec);
if (codec->patch_ops.resume)
codec->patch_ops.resume(codec);
else {
@@ -1919,23 +2086,31 @@ int __devinit snd_hda_build_controls(struct hda_bus *bus)
struct hda_codec *codec;
list_for_each_entry(codec, &bus->codec_list, list) {
- int err = 0;
- /* fake as if already powered-on */
- hda_keep_power_on(codec);
- /* then fire up */
- hda_set_power_state(codec,
- codec->afg ? codec->afg : codec->mfg,
- AC_PWRST_D0);
- /* continue to initialize... */
- if (codec->patch_ops.init)
- err = codec->patch_ops.init(codec);
- if (!err && codec->patch_ops.build_controls)
- err = codec->patch_ops.build_controls(codec);
- snd_hda_power_down(codec);
+ int err = snd_hda_codec_build_controls(codec);
if (err < 0)
return err;
}
+ return 0;
+}
+int snd_hda_codec_build_controls(struct hda_codec *codec)
+{
+ int err = 0;
+ /* fake as if already powered-on */
+ hda_keep_power_on(codec);
+ /* then fire up */
+ hda_set_power_state(codec,
+ codec->afg ? codec->afg : codec->mfg,
+ AC_PWRST_D0);
+ hda_exec_init_verbs(codec);
+ /* continue to initialize... */
+ if (codec->patch_ops.init)
+ err = codec->patch_ops.init(codec);
+ if (!err && codec->patch_ops.build_controls)
+ err = codec->patch_ops.build_controls(codec);
+ snd_hda_power_down(codec);
+ if (err < 0)
+ return err;
return 0;
}
@@ -2236,8 +2411,8 @@ static int hda_pcm_default_cleanup(struct hda_pcm_stream *hinfo,
return 0;
}
-static int __devinit set_pcm_default_values(struct hda_codec *codec,
- struct hda_pcm_stream *info)
+static int set_pcm_default_values(struct hda_codec *codec,
+ struct hda_pcm_stream *info)
{
/* query support PCM information from the given NID */
if (info->nid && (!info->rates || !info->formats)) {
@@ -2263,6 +2438,29 @@ static int __devinit set_pcm_default_values(struct hda_codec *codec,
return 0;
}
+/*
+ * attach a new PCM stream
+ */
+static int __devinit
+snd_hda_attach_pcm(struct hda_codec *codec, struct hda_pcm *pcm)
+{
+ struct hda_bus *bus = codec->bus;
+ struct hda_pcm_stream *info;
+ int stream, err;
+
+ if (snd_BUG_ON(!pcm->name))
+ return -EINVAL;
+ for (stream = 0; stream < 2; stream++) {
+ info = &pcm->stream[stream];
+ if (info->substreams) {
+ err = set_pcm_default_values(codec, info);
+ if (err < 0)
+ return err;
+ }
+ }
+ return bus->ops.attach_pcm(bus, codec, pcm);
+}
+
/**
* snd_hda_build_pcms - build PCM information
* @bus: the BUS
@@ -2289,25 +2487,72 @@ static int __devinit set_pcm_default_values(struct hda_codec *codec,
*
* This function returns 0 if successfull, or a negative error code.
*/
-int __devinit snd_hda_build_pcms(struct hda_bus *bus)
+int snd_hda_build_pcms(struct hda_bus *bus)
{
+ 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 num_devs[HDA_PCM_NTYPES];
+ memset(num_devs, 0, sizeof(num_devs));
list_for_each_entry(codec, &bus->codec_list, list) {
- unsigned int pcm, s;
+ unsigned int pcm;
int err;
- if (!codec->patch_ops.build_pcms)
- continue;
- err = codec->patch_ops.build_pcms(codec);
- if (err < 0)
- return err;
+ if (!codec->num_pcms) {
+ 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)
+ struct hda_pcm *cpcm = &codec->pcm_info[pcm];
+ int type = cpcm->pcm_type;
+ int dev;
+
+ if (!cpcm->stream[0].substreams &&
+ !cpcm->stream[1].substreams)
+ continue; /* no substreams assigned */
+
+ 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;
- err = set_pcm_default_values(codec, info);
+ }
+ dev = 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;
+ }
+ dev = dev_idx[type];
+ break;
+ default:
+ snd_printk(KERN_WARNING
+ "Invalid PCM type %d\n", type);
+ continue;
+ }
+ num_devs[type]++;
+ if (!cpcm->pcm) {
+ cpcm->device = dev;
+ err = snd_hda_attach_pcm(codec, cpcm);
if (err < 0)
return err;
}
@@ -2333,11 +2578,11 @@ int snd_hda_check_board_config(struct hda_codec *codec,
int num_configs, const char **models,
const struct snd_pci_quirk *tbl)
{
- if (codec->bus->modelname && models) {
+ if (codec->modelname && models) {
int i;
for (i = 0; i < num_configs; i++) {
if (models[i] &&
- !strcmp(codec->bus->modelname, models[i])) {
+ !strcmp(codec->modelname, models[i])) {
snd_printd(KERN_INFO "hda_codec: model '%s' is "
"selected\n", models[i]);
return i;
@@ -2390,7 +2635,7 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
kctl = snd_ctl_new1(knew, codec);
if (!kctl)
return -ENOMEM;
- err = snd_ctl_add(codec->bus->card, kctl);
+ err = snd_hda_ctl_add(codec, kctl);
if (err < 0) {
if (!codec->addr)
return err;
@@ -2398,7 +2643,7 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
if (!kctl)
return -ENOMEM;
kctl->id.device = codec->addr;
- err = snd_ctl_add(codec->bus->card, kctl);
+ err = snd_hda_ctl_add(codec, kctl);
if (err < 0)
return err;
}
@@ -2414,6 +2659,7 @@ static void hda_power_work(struct work_struct *work)
{
struct hda_codec *codec =
container_of(work, struct hda_codec, power_work.work);
+ struct hda_bus *bus = codec->bus;
if (!codec->power_on || codec->power_count) {
codec->power_transition = 0;
@@ -2421,8 +2667,8 @@ static void hda_power_work(struct work_struct *work)
}
hda_call_codec_suspend(codec);
- if (codec->bus->ops.pm_notify)
- codec->bus->ops.pm_notify(codec);
+ if (bus->ops.pm_notify)
+ bus->ops.pm_notify(bus);
}
static void hda_keep_power_on(struct hda_codec *codec)
@@ -2433,13 +2679,15 @@ static void hda_keep_power_on(struct hda_codec *codec)
void snd_hda_power_up(struct hda_codec *codec)
{
+ struct hda_bus *bus = codec->bus;
+
codec->power_count++;
if (codec->power_on || codec->power_transition)
return;
codec->power_on = 1;
- if (codec->bus->ops.pm_notify)
- codec->bus->ops.pm_notify(codec);
+ if (bus->ops.pm_notify)
+ bus->ops.pm_notify(bus);
hda_call_codec_resume(codec);
cancel_delayed_work(&codec->power_work);
codec->power_transition = 0;
@@ -3139,3 +3387,40 @@ int snd_hda_codecs_inuse(struct hda_bus *bus)
}
#endif
#endif
+
+/*
+ * generic arrays
+ */
+
+/* get a new element from the given array
+ * if it exceeds the pre-allocated array size, re-allocate the array
+ */
+void *snd_array_new(struct snd_array *array)
+{
+ if (array->used >= array->alloced) {
+ int num = array->alloced + array->alloc_align;
+ void *nlist;
+ if (snd_BUG_ON(num >= 4096))
+ return NULL;
+ nlist = kcalloc(num + 1, array->elem_size, GFP_KERNEL);
+ if (!nlist)
+ return NULL;
+ if (array->list) {
+ memcpy(nlist, array->list,
+ array->elem_size * array->alloced);
+ kfree(array->list);
+ }
+ array->list = nlist;
+ array->alloced = num;
+ }
+ return snd_array_elem(array, array->used++);
+}
+
+/* free the given array elements */
+void snd_array_free(struct snd_array *array)
+{
+ kfree(array->list);
+ array->used = 0;
+ array->alloced = 0;
+ array->list = NULL;
+}
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 60468f562400..ee122b009fd4 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -520,6 +520,36 @@ enum {
#define HDA_MAX_CODEC_ADDRESS 0x0f
/*
+ * generic arrays
+ */
+struct snd_array {
+ unsigned int used;
+ unsigned int alloced;
+ unsigned int elem_size;
+ unsigned int alloc_align;
+ void *list;
+};
+
+void *snd_array_new(struct snd_array *array);
+void snd_array_free(struct snd_array *array);
+static inline void snd_array_init(struct snd_array *array, unsigned int size,
+ unsigned int align)
+{
+ array->elem_size = size;
+ array->alloc_align = align;
+}
+
+static inline void *snd_array_elem(struct snd_array *array, unsigned int idx)
+{
+ return array->list + idx * array->elem_size;
+}
+
+static inline unsigned int snd_array_index(struct snd_array *array, void *ptr)
+{
+ return (unsigned long)(ptr - array->list) / array->elem_size;
+}
+
+/*
* Structures
*/
@@ -536,15 +566,17 @@ typedef u16 hda_nid_t;
/* bus operators */
struct hda_bus_ops {
/* send a single command */
- int (*command)(struct hda_codec *codec, hda_nid_t nid, int direct,
- unsigned int verb, unsigned int parm);
+ int (*command)(struct hda_bus *bus, unsigned int cmd);
/* get a response from the last command */
- unsigned int (*get_response)(struct hda_codec *codec);
+ unsigned int (*get_response)(struct hda_bus *bus);
/* free the private data */
void (*private_free)(struct hda_bus *);
+ /* attach a PCM stream */
+ int (*attach_pcm)(struct hda_bus *bus, struct hda_codec *codec,
+ struct hda_pcm *pcm);
#ifdef CONFIG_SND_HDA_POWER_SAVE
/* notify power-up/down from codec to controller */
- void (*pm_notify)(struct hda_codec *codec);
+ void (*pm_notify)(struct hda_bus *bus);
#endif
};
@@ -635,10 +667,7 @@ struct hda_amp_info {
struct hda_cache_rec {
u16 hash[64]; /* hash table for index */
- unsigned int num_entries; /* number of assigned entries */
- unsigned int size; /* allocated size */
- unsigned int record_size; /* record size (including header) */
- void *buffer; /* hash table entries */
+ struct snd_array buf; /* record entries */
};
/* PCM callbacks */
@@ -680,7 +709,8 @@ struct hda_pcm {
char *name;
struct hda_pcm_stream stream[2];
unsigned int pcm_type; /* HDA_PCM_TYPE_XXX */
- int device; /* assigned device number */
+ int device; /* device number to assign */
+ struct snd_pcm *pcm; /* assigned PCM instance */
};
/* codec information */
@@ -699,6 +729,8 @@ struct hda_codec {
/* detected preset */
const struct hda_codec_preset *preset;
+ const char *name; /* codec name */
+ const char *modelname; /* model name for preset */
/* set by patch */
struct hda_codec_ops patch_ops;
@@ -718,6 +750,8 @@ struct hda_codec {
hda_nid_t start_nid;
u32 *wcaps;
+ struct snd_array mixers; /* list of assigned mixer elements */
+
struct hda_cache_rec amp_cache; /* cache for amp access */
struct hda_cache_rec cmd_cache; /* cache for other commands */
@@ -727,7 +761,11 @@ struct hda_codec {
unsigned int spdif_in_enable; /* SPDIF input enable? */
hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */
+#ifdef CONFIG_SND_HDA_HWDEP
struct snd_hwdep *hwdep; /* assigned hwdep device */
+ struct snd_array init_verbs; /* additional init verbs */
+ struct snd_array hints; /* additional hints */
+#endif
/* misc flags */
unsigned int spdif_status_reset :1; /* needs to toggle SPDIF for each
@@ -799,6 +837,7 @@ void snd_hda_codec_resume_cache(struct hda_codec *codec);
* Mixer
*/
int snd_hda_build_controls(struct hda_bus *bus);
+int snd_hda_codec_build_controls(struct hda_codec *codec);
/*
* PCM
@@ -831,6 +870,13 @@ int snd_hda_resume(struct hda_bus *bus);
#endif
/*
+ * get widget information
+ */
+const char *snd_hda_get_jack_connectivity(u32 cfg);
+const char *snd_hda_get_jack_type(u32 cfg);
+const char *snd_hda_get_jack_location(u32 cfg);
+
+/*
* power saving
*/
#ifdef CONFIG_SND_HDA_POWER_SAVE
diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c
new file mode 100644
index 000000000000..7fa065cd1d9c
--- /dev/null
+++ b/sound/pci/hda/hda_eld.c
@@ -0,0 +1,528 @@
+/*
+ * Generic routines and proc interface for ELD(EDID Like Data) information
+ *
+ * Copyright(c) 2008 Intel Corporation.
+ *
+ * Authors:
+ * Wu Fengguang <wfg@linux.intel.com>
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <sound/core.h>
+#include <asm/unaligned.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+enum eld_versions {
+ ELD_VER_CEA_861D = 2,
+ ELD_VER_PARTIAL = 31,
+};
+
+static char *eld_versoin_names[32] = {
+ "reserved",
+ "reserved",
+ "CEA-861D or below",
+ [3 ... 30] = "reserved",
+ [31] = "partial"
+};
+
+enum cea_edid_versions {
+ CEA_EDID_VER_NONE = 0,
+ CEA_EDID_VER_CEA861 = 1,
+ CEA_EDID_VER_CEA861A = 2,
+ CEA_EDID_VER_CEA861BCD = 3,
+ CEA_EDID_VER_RESERVED = 4,
+};
+
+static char *cea_edid_version_names[8] = {
+ "no CEA EDID Timing Extension block present",
+ "CEA-861",
+ "CEA-861-A",
+ "CEA-861-B, C or D",
+ [4 ... 7] = "reserved"
+};
+
+static char *cea_speaker_allocation_names[] = {
+ /* 0 */ "FL/FR",
+ /* 1 */ "LFE",
+ /* 2 */ "FC",
+ /* 3 */ "RL/RR",
+ /* 4 */ "RC",
+ /* 5 */ "FLC/FRC",
+ /* 6 */ "RLC/RRC",
+ /* 7 */ "FLW/FRW",
+ /* 8 */ "FLH/FRH",
+ /* 9 */ "TC",
+ /* 10 */ "FCH",
+};
+
+static char *eld_connection_type_names[4] = {
+ "HDMI",
+ "Display Port",
+ "2-reserved",
+ "3-reserved"
+};
+
+enum cea_audio_coding_types {
+ AUDIO_CODING_TYPE_REF_STREAM_HEADER = 0,
+ AUDIO_CODING_TYPE_LPCM = 1,
+ AUDIO_CODING_TYPE_AC3 = 2,
+ AUDIO_CODING_TYPE_MPEG1 = 3,
+ AUDIO_CODING_TYPE_MP3 = 4,
+ AUDIO_CODING_TYPE_MPEG2 = 5,
+ AUDIO_CODING_TYPE_AACLC = 6,
+ AUDIO_CODING_TYPE_DTS = 7,
+ AUDIO_CODING_TYPE_ATRAC = 8,
+ AUDIO_CODING_TYPE_SACD = 9,
+ AUDIO_CODING_TYPE_EAC3 = 10,
+ AUDIO_CODING_TYPE_DTS_HD = 11,
+ AUDIO_CODING_TYPE_MLP = 12,
+ AUDIO_CODING_TYPE_DST = 13,
+ AUDIO_CODING_TYPE_WMAPRO = 14,
+ AUDIO_CODING_TYPE_REF_CXT = 15,
+ /* also include valid xtypes below */
+ AUDIO_CODING_TYPE_HE_AAC = 15,
+ AUDIO_CODING_TYPE_HE_AAC2 = 16,
+ AUDIO_CODING_TYPE_MPEG_SURROUND = 17,
+};
+
+enum cea_audio_coding_xtypes {
+ AUDIO_CODING_XTYPE_HE_REF_CT = 0,
+ AUDIO_CODING_XTYPE_HE_AAC = 1,
+ AUDIO_CODING_XTYPE_HE_AAC2 = 2,
+ AUDIO_CODING_XTYPE_MPEG_SURROUND = 3,
+ AUDIO_CODING_XTYPE_FIRST_RESERVED = 4,
+};
+
+static char *cea_audio_coding_type_names[] = {
+ /* 0 */ "undefined",
+ /* 1 */ "LPCM",
+ /* 2 */ "AC-3",
+ /* 3 */ "MPEG1",
+ /* 4 */ "MP3",
+ /* 5 */ "MPEG2",
+ /* 6 */ "AAC-LC",
+ /* 7 */ "DTS",
+ /* 8 */ "ATRAC",
+ /* 9 */ "DSD (1-bit audio)",
+ /* 10 */ "E-AC-3/DD+ (Dolby Digital Plus)",
+ /* 11 */ "DTS-HD",
+ /* 12 */ "MLP (Dolby TrueHD)",
+ /* 13 */ "DST",
+ /* 14 */ "WMAPro",
+ /* 15 */ "HE-AAC",
+ /* 16 */ "HE-AACv2",
+ /* 17 */ "MPEG Surround",
+};
+
+/*
+ * The following two lists are shared between
+ * - HDMI audio InfoFrame (source to sink)
+ * - CEA E-EDID extension (sink to source)
+ */
+
+/*
+ * SS1:SS0 index => sample size
+ */
+static int cea_sample_sizes[4] = {
+ 0, /* 0: Refer to Stream Header */
+ AC_SUPPCM_BITS_16, /* 1: 16 bits */
+ AC_SUPPCM_BITS_20, /* 2: 20 bits */
+ AC_SUPPCM_BITS_24, /* 3: 24 bits */
+};
+
+/*
+ * SF2:SF1:SF0 index => sampling frequency
+ */
+static int cea_sampling_frequencies[8] = {
+ 0, /* 0: Refer to Stream Header */
+ SNDRV_PCM_RATE_32000, /* 1: 32000Hz */
+ SNDRV_PCM_RATE_44100, /* 2: 44100Hz */
+ SNDRV_PCM_RATE_48000, /* 3: 48000Hz */
+ SNDRV_PCM_RATE_88200, /* 4: 88200Hz */
+ SNDRV_PCM_RATE_96000, /* 5: 96000Hz */
+ SNDRV_PCM_RATE_176400, /* 6: 176400Hz */
+ SNDRV_PCM_RATE_192000, /* 7: 192000Hz */
+};
+
+static unsigned char hdmi_get_eld_byte(struct hda_codec *codec, hda_nid_t nid,
+ int byte_index)
+{
+ unsigned int val;
+
+ val = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_HDMI_ELDD, byte_index);
+
+#ifdef BE_PARANOID
+ printk(KERN_INFO "ELD data byte %d: 0x%x\n", byte_index, val);
+#endif
+
+ if ((val & AC_ELDD_ELD_VALID) == 0) {
+ snd_printd(KERN_INFO "Invalid ELD data byte %d\n",
+ byte_index);
+ val = 0;
+ }
+
+ return val & AC_ELDD_ELD_DATA;
+}
+
+#define GRAB_BITS(buf, byte, lowbit, bits) \
+({ \
+ BUILD_BUG_ON(lowbit > 7); \
+ BUILD_BUG_ON(bits > 8); \
+ BUILD_BUG_ON(bits <= 0); \
+ \
+ (buf[byte] >> (lowbit)) & ((1 << (bits)) - 1); \
+})
+
+static void hdmi_update_short_audio_desc(struct cea_sad *a,
+ const unsigned char *buf)
+{
+ int i;
+ int val;
+
+ val = GRAB_BITS(buf, 1, 0, 7);
+ a->rates = 0;
+ for (i = 0; i < 7; i++)
+ if (val & (1 << i))
+ a->rates |= cea_sampling_frequencies[i + 1];
+
+ a->channels = GRAB_BITS(buf, 0, 0, 3);
+ a->channels++;
+
+ a->format = GRAB_BITS(buf, 0, 3, 4);
+ switch (a->format) {
+ case AUDIO_CODING_TYPE_REF_STREAM_HEADER:
+ snd_printd(KERN_INFO
+ "audio coding type 0 not expected in ELD\n");
+ break;
+
+ case AUDIO_CODING_TYPE_LPCM:
+ val = GRAB_BITS(buf, 2, 0, 3);
+ a->sample_bits = 0;
+ for (i = 0; i < 3; i++)
+ if (val & (1 << i))
+ a->sample_bits |= cea_sample_sizes[i + 1];
+ break;
+
+ case AUDIO_CODING_TYPE_AC3:
+ case AUDIO_CODING_TYPE_MPEG1:
+ case AUDIO_CODING_TYPE_MP3:
+ case AUDIO_CODING_TYPE_MPEG2:
+ case AUDIO_CODING_TYPE_AACLC:
+ case AUDIO_CODING_TYPE_DTS:
+ case AUDIO_CODING_TYPE_ATRAC:
+ a->max_bitrate = GRAB_BITS(buf, 2, 0, 8);
+ a->max_bitrate *= 8000;
+ break;
+
+ case AUDIO_CODING_TYPE_SACD:
+ break;
+
+ case AUDIO_CODING_TYPE_EAC3:
+ break;
+
+ case AUDIO_CODING_TYPE_DTS_HD:
+ break;
+
+ case AUDIO_CODING_TYPE_MLP:
+ break;
+
+ case AUDIO_CODING_TYPE_DST:
+ break;
+
+ case AUDIO_CODING_TYPE_WMAPRO:
+ a->profile = GRAB_BITS(buf, 2, 0, 3);
+ break;
+
+ case AUDIO_CODING_TYPE_REF_CXT:
+ a->format = GRAB_BITS(buf, 2, 3, 5);
+ if (a->format == AUDIO_CODING_XTYPE_HE_REF_CT ||
+ a->format >= AUDIO_CODING_XTYPE_FIRST_RESERVED) {
+ snd_printd(KERN_INFO
+ "audio coding xtype %d not expected in ELD\n",
+ a->format);
+ a->format = 0;
+ } else
+ a->format += AUDIO_CODING_TYPE_HE_AAC -
+ AUDIO_CODING_XTYPE_HE_AAC;
+ break;
+ }
+}
+
+/*
+ * Be careful, ELD buf could be totally rubbish!
+ */
+static int hdmi_update_sink_eld(struct sink_eld *e,
+ const unsigned char *buf, int size)
+{
+ int mnl;
+ int i;
+
+ e->eld_ver = GRAB_BITS(buf, 0, 3, 5);
+ if (e->eld_ver != ELD_VER_CEA_861D &&
+ e->eld_ver != ELD_VER_PARTIAL) {
+ snd_printd(KERN_INFO "Unknown ELD version %d\n", e->eld_ver);
+ goto out_fail;
+ }
+
+ e->eld_size = size;
+ e->baseline_len = GRAB_BITS(buf, 2, 0, 8);
+ mnl = GRAB_BITS(buf, 4, 0, 5);
+ e->cea_edid_ver = GRAB_BITS(buf, 4, 5, 3);
+
+ e->support_hdcp = GRAB_BITS(buf, 5, 0, 1);
+ e->support_ai = GRAB_BITS(buf, 5, 1, 1);
+ e->conn_type = GRAB_BITS(buf, 5, 2, 2);
+ e->sad_count = GRAB_BITS(buf, 5, 4, 4);
+
+ e->aud_synch_delay = GRAB_BITS(buf, 6, 0, 8) * 2;
+ e->spk_alloc = GRAB_BITS(buf, 7, 0, 7);
+
+ e->port_id = get_unaligned_le64(buf + 8);
+
+ /* not specified, but the spec's tendency is little endian */
+ e->manufacture_id = get_unaligned_le16(buf + 16);
+ e->product_id = get_unaligned_le16(buf + 18);
+
+ if (mnl > ELD_MAX_MNL) {
+ snd_printd(KERN_INFO "MNL is reserved value %d\n", mnl);
+ goto out_fail;
+ } else if (ELD_FIXED_BYTES + mnl > size) {
+ snd_printd(KERN_INFO "out of range MNL %d\n", mnl);
+ goto out_fail;
+ } else
+ strlcpy(e->monitor_name, buf + ELD_FIXED_BYTES, mnl);
+
+ for (i = 0; i < e->sad_count; i++) {
+ if (ELD_FIXED_BYTES + mnl + 3 * (i + 1) > size) {
+ snd_printd(KERN_INFO "out of range SAD %d\n", i);
+ goto out_fail;
+ }
+ hdmi_update_short_audio_desc(e->sad + i,
+ buf + ELD_FIXED_BYTES + mnl + 3 * i);
+ }
+
+ return 0;
+
+out_fail:
+ e->eld_ver = 0;
+ return -EINVAL;
+}
+
+static int hdmi_present_sense(struct hda_codec *codec, hda_nid_t nid)
+{
+ return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0);
+}
+
+static int hdmi_eld_valid(struct hda_codec *codec, hda_nid_t nid)
+{
+ int eldv;
+ int present;
+
+ present = hdmi_present_sense(codec, nid);
+ eldv = (present & AC_PINSENSE_ELDV);
+ present = (present & AC_PINSENSE_PRESENCE);
+
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ printk(KERN_INFO "pinp = %d, eldv = %d\n", !!present, !!eldv);
+#endif
+
+ return eldv && present;
+}
+
+int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid)
+{
+ return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE,
+ AC_DIPSIZE_ELD_BUF);
+}
+
+int snd_hdmi_get_eld(struct sink_eld *eld,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ int i;
+ int ret;
+ int size;
+ unsigned char *buf;
+
+ if (!hdmi_eld_valid(codec, nid))
+ return -ENOENT;
+
+ size = snd_hdmi_get_eld_size(codec, nid);
+ if (size == 0) {
+ /* wfg: workaround for ASUS P5E-VM HDMI board */
+ snd_printd(KERN_INFO "ELD buf size is 0, force 128\n");
+ size = 128;
+ }
+ if (size < ELD_FIXED_BYTES || size > PAGE_SIZE) {
+ snd_printd(KERN_INFO "Invalid ELD buf size %d\n", size);
+ return -ERANGE;
+ }
+
+ buf = kmalloc(size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ for (i = 0; i < size; i++)
+ buf[i] = hdmi_get_eld_byte(codec, nid, i);
+
+ ret = hdmi_update_sink_eld(eld, buf, size);
+
+ kfree(buf);
+ return ret;
+}
+
+static void hdmi_show_short_audio_desc(struct cea_sad *a)
+{
+ char buf[SND_PRINT_RATES_ADVISED_BUFSIZE];
+
+ printk(KERN_INFO "coding type: %s\n",
+ cea_audio_coding_type_names[a->format]);
+ printk(KERN_INFO "channels: %d\n", a->channels);
+
+ snd_print_pcm_rates(a->rates, buf, sizeof(buf));
+ printk(KERN_INFO "sampling frequencies: %s\n", buf);
+
+ if (a->format == AUDIO_CODING_TYPE_LPCM)
+ printk(KERN_INFO "sample bits: 0x%x\n", a->sample_bits);
+
+ if (a->max_bitrate)
+ printk(KERN_INFO "max bitrate: %d\n", a->max_bitrate);
+
+ if (a->profile)
+ printk(KERN_INFO "profile: %d\n", a->profile);
+}
+
+#define HDMI_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80
+static void hdmi_print_channel_allocation(int spk_alloc, char *buf, int buflen)
+{
+ int i, j;
+
+ for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) {
+ if (spk_alloc & (1 << i))
+ j += snprintf(buf + j, buflen - j, "%s ",
+ cea_speaker_allocation_names[i]);
+ }
+ if (j)
+ j--; /* skip last space */
+ buf[j] = '\0'; /* necessary when j == 0 */
+}
+
+void snd_hdmi_show_eld(struct sink_eld *e)
+{
+ int i;
+ char buf[HDMI_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
+
+ printk(KERN_INFO "ELD buffer size is %d\n", e->eld_size);
+ printk(KERN_INFO "ELD baseline len is %d*4\n", e->baseline_len);
+ printk(KERN_INFO "vendor block len is %d\n",
+ e->eld_size - e->baseline_len * 4 - 4);
+ printk(KERN_INFO "ELD version is %s\n",
+ eld_versoin_names[e->eld_ver]);
+ printk(KERN_INFO "CEA EDID version is %s\n",
+ cea_edid_version_names[e->cea_edid_ver]);
+ printk(KERN_INFO "manufacture id is 0x%x\n", e->manufacture_id);
+ printk(KERN_INFO "product id is 0x%x\n", e->product_id);
+ printk(KERN_INFO "port id is 0x%llx\n", (long long)e->port_id);
+ printk(KERN_INFO "HDCP support is %d\n", e->support_hdcp);
+ printk(KERN_INFO "AI support is %d\n", e->support_ai);
+ printk(KERN_INFO "SAD count is %d\n", e->sad_count);
+ printk(KERN_INFO "audio sync delay is %x\n", e->aud_synch_delay);
+ printk(KERN_INFO "connection type is %s\n",
+ eld_connection_type_names[e->conn_type]);
+ printk(KERN_INFO "monitor name is %s\n", e->monitor_name);
+
+ hdmi_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
+ printk(KERN_INFO "speaker allocations: (0x%x)%s\n", e->spk_alloc, buf);
+
+ for (i = 0; i < e->sad_count; i++)
+ hdmi_show_short_audio_desc(e->sad + i);
+}
+
+#ifdef CONFIG_PROC_FS
+
+static void hdmi_print_sad_info(int i, struct cea_sad *a,
+ struct snd_info_buffer *buffer)
+{
+ char buf[80];
+
+ snd_iprintf(buffer, "sad%d_coding_type\t[0x%x] %s\n",
+ i, a->format, cea_audio_coding_type_names[a->format]);
+ snd_iprintf(buffer, "sad%d_channels\t\t%d\n", i, a->channels);
+
+ snd_print_pcm_rates(a->rates, buf, sizeof(buf));
+ snd_iprintf(buffer, "sad%d_sampling_rates\t[0x%x] %s\n",
+ i, a->rates, buf);
+
+ if (a->format == AUDIO_CODING_TYPE_LPCM)
+ snd_iprintf(buffer, "sad%d_sample_bits\t0x%x\n",
+ i, a->sample_bits);
+
+ if (a->max_bitrate)
+ snd_iprintf(buffer, "sad%d_max_bitrate\t%d\n",
+ i, a->max_bitrate);
+
+ if (a->profile)
+ snd_iprintf(buffer, "sad%d_profile\t\t%d\n", i, a->profile);
+}
+
+static void hdmi_print_eld_info(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct sink_eld *e = entry->private_data;
+ char buf[HDMI_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
+ int i;
+
+ snd_iprintf(buffer, "monitor name\t\t%s\n", e->monitor_name);
+ snd_iprintf(buffer, "connection_type\t\t%s\n",
+ eld_connection_type_names[e->conn_type]);
+ snd_iprintf(buffer, "eld_version\t\t[0x%x] %s\n", e->eld_ver,
+ eld_versoin_names[e->eld_ver]);
+ snd_iprintf(buffer, "edid_version\t\t[0x%x] %s\n", e->cea_edid_ver,
+ cea_edid_version_names[e->cea_edid_ver]);
+ snd_iprintf(buffer, "manufacture_id\t\t0x%x\n", e->manufacture_id);
+ snd_iprintf(buffer, "product_id\t\t0x%x\n", e->product_id);
+ snd_iprintf(buffer, "port_id\t\t\t0x%llx\n", (long long)e->port_id);
+ snd_iprintf(buffer, "support_hdcp\t\t%d\n", e->support_hdcp);
+ snd_iprintf(buffer, "support_ai\t\t%d\n", e->support_ai);
+ snd_iprintf(buffer, "audio_sync_delay\t%d\n", e->aud_synch_delay);
+
+ hdmi_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
+ snd_iprintf(buffer, "speakers\t\t[0x%x] %s\n", e->spk_alloc, buf);
+
+ snd_iprintf(buffer, "sad_count\t\t%d\n", e->sad_count);
+
+ for (i = 0; i < e->sad_count; i++)
+ hdmi_print_sad_info(i, e->sad + i, buffer);
+}
+
+int snd_hda_eld_proc_new(struct hda_codec *codec, struct sink_eld *eld)
+{
+ char name[32];
+ struct snd_info_entry *entry;
+ int err;
+
+ snprintf(name, sizeof(name), "eld#%d", codec->addr);
+ err = snd_card_proc_new(codec->bus->card, name, &entry);
+ if (err < 0)
+ return err;
+
+ snd_info_set_text_ops(entry, eld, hdmi_print_eld_info);
+ return 0;
+}
+
+#endif
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 0ca30894f7c6..98ff010d5b95 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -723,7 +723,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
if (is_loopback)
add_input_loopback(codec, node->nid, HDA_INPUT, index);
snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
- if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
+ err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+ if (err < 0)
return err;
created = 1;
} else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
@@ -732,7 +733,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
if (is_loopback)
add_input_loopback(codec, node->nid, HDA_OUTPUT, 0);
snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
- if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
+ err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+ if (err < 0)
return err;
created = 1;
}
@@ -745,14 +747,16 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
(node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) {
knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, index, HDA_INPUT);
snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
- if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
+ err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+ if (err < 0)
return err;
created = 1;
} else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
(node->amp_out_caps & AC_AMPCAP_NUM_STEPS)) {
knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, 0, HDA_OUTPUT);
snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
- if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
+ err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+ if (err < 0)
return err;
created = 1;
}
@@ -849,8 +853,8 @@ static int build_input_controls(struct hda_codec *codec)
}
/* create input MUX if multiple sources are available */
- if ((err = snd_ctl_add(codec->bus->card,
- snd_ctl_new1(&cap_sel, codec))) < 0)
+ err = snd_hda_ctl_add(codec, snd_ctl_new1(&cap_sel, codec));
+ if (err < 0)
return err;
/* no volume control? */
@@ -867,8 +871,8 @@ static int build_input_controls(struct hda_codec *codec)
HDA_CODEC_VOLUME(name, adc_node->nid,
spec->input_mux.items[i].index,
HDA_INPUT);
- if ((err = snd_ctl_add(codec->bus->card,
- snd_ctl_new1(&knew, codec))) < 0)
+ err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+ if (err < 0)
return err;
}
diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c
index 6e18a422d993..653da1d3e4df 100644
--- a/sound/pci/hda/hda_hwdep.c
+++ b/sound/pci/hda/hda_hwdep.c
@@ -23,10 +23,12 @@
#include <linux/pci.h>
#include <linux/compat.h>
#include <linux/mutex.h>
+#include <linux/ctype.h>
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
#include <sound/hda_hwdep.h>
+#include <sound/minors.h>
/*
* write/read an out-of-bound verb
@@ -95,6 +97,25 @@ static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file)
return 0;
}
+static void clear_hwdep_elements(struct hda_codec *codec)
+{
+ char **head;
+ int i;
+
+ /* clear init verbs */
+ snd_array_free(&codec->init_verbs);
+ /* clear hints */
+ head = codec->hints.list;
+ for (i = 0; i < codec->hints.used; i++, head++)
+ kfree(*head);
+ snd_array_free(&codec->hints);
+}
+
+static void hwdep_free(struct snd_hwdep *hwdep)
+{
+ clear_hwdep_elements(hwdep->private_data);
+}
+
int __devinit snd_hda_create_hwdep(struct hda_codec *codec)
{
char hwname[16];
@@ -109,6 +130,7 @@ int __devinit snd_hda_create_hwdep(struct hda_codec *codec)
sprintf(hwdep->name, "HDA Codec %d", codec->addr);
hwdep->iface = SNDRV_HWDEP_IFACE_HDA;
hwdep->private_data = codec;
+ hwdep->private_free = hwdep_free;
hwdep->exclusive = 1;
hwdep->ops.open = hda_hwdep_open;
@@ -117,5 +139,211 @@ int __devinit snd_hda_create_hwdep(struct hda_codec *codec)
hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat;
#endif
+ snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32);
+ snd_array_init(&codec->hints, sizeof(char *), 32);
+
+ return 0;
+}
+
+/*
+ * sysfs interface
+ */
+
+static int clear_codec(struct hda_codec *codec)
+{
+ snd_hda_codec_reset(codec);
+ clear_hwdep_elements(codec);
+ return 0;
+}
+
+static int reconfig_codec(struct hda_codec *codec)
+{
+ int err;
+
+ snd_printk(KERN_INFO "hda-codec: reconfiguring\n");
+ snd_hda_codec_reset(codec);
+ err = snd_hda_codec_configure(codec);
+ if (err < 0)
+ return err;
+ /* rebuild PCMs */
+ err = snd_hda_build_pcms(codec->bus);
+ if (err < 0)
+ return err;
+ /* rebuild mixers */
+ err = snd_hda_codec_build_controls(codec);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+/*
+ * allocate a string at most len chars, and remove the trailing EOL
+ */
+static char *kstrndup_noeol(const char *src, size_t len)
+{
+ char *s = kstrndup(src, len, GFP_KERNEL);
+ char *p;
+ if (!s)
+ return NULL;
+ p = strchr(s, '\n');
+ if (p)
+ *p = 0;
+ return s;
+}
+
+#define CODEC_INFO_SHOW(type) \
+static ssize_t type##_show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
+ struct hda_codec *codec = hwdep->private_data; \
+ return sprintf(buf, "0x%x\n", codec->type); \
+}
+
+#define CODEC_INFO_STR_SHOW(type) \
+static ssize_t type##_show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
+ struct hda_codec *codec = hwdep->private_data; \
+ return sprintf(buf, "%s\n", \
+ codec->type ? codec->type : ""); \
+}
+
+CODEC_INFO_SHOW(vendor_id);
+CODEC_INFO_SHOW(subsystem_id);
+CODEC_INFO_SHOW(revision_id);
+CODEC_INFO_SHOW(afg);
+CODEC_INFO_SHOW(mfg);
+CODEC_INFO_STR_SHOW(name);
+CODEC_INFO_STR_SHOW(modelname);
+
+#define CODEC_INFO_STORE(type) \
+static ssize_t type##_store(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
+ struct hda_codec *codec = hwdep->private_data; \
+ char *after; \
+ codec->type = simple_strtoul(buf, &after, 0); \
+ return count; \
+}
+
+#define CODEC_INFO_STR_STORE(type) \
+static ssize_t type##_store(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
+ struct hda_codec *codec = hwdep->private_data; \
+ char *s = kstrndup_noeol(buf, 64); \
+ if (!s) \
+ return -ENOMEM; \
+ kfree(codec->type); \
+ codec->type = s; \
+ return count; \
+}
+
+CODEC_INFO_STORE(vendor_id);
+CODEC_INFO_STORE(subsystem_id);
+CODEC_INFO_STORE(revision_id);
+CODEC_INFO_STR_STORE(name);
+CODEC_INFO_STR_STORE(modelname);
+
+#define CODEC_ACTION_STORE(type) \
+static ssize_t type##_store(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
+ struct hda_codec *codec = hwdep->private_data; \
+ int err = 0; \
+ if (*buf) \
+ err = type##_codec(codec); \
+ return err < 0 ? err : count; \
+}
+
+CODEC_ACTION_STORE(reconfig);
+CODEC_ACTION_STORE(clear);
+
+static ssize_t init_verbs_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+ struct hda_codec *codec = hwdep->private_data;
+ char *p;
+ struct hda_verb verb, *v;
+
+ verb.nid = simple_strtoul(buf, &p, 0);
+ verb.verb = simple_strtoul(p, &p, 0);
+ verb.param = simple_strtoul(p, &p, 0);
+ if (!verb.nid || !verb.verb || !verb.param)
+ return -EINVAL;
+ v = snd_array_new(&codec->init_verbs);
+ if (!v)
+ return -ENOMEM;
+ *v = verb;
+ return count;
+}
+
+static ssize_t hints_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+ struct hda_codec *codec = hwdep->private_data;
+ char *p;
+ char **hint;
+
+ if (!*buf || isspace(*buf) || *buf == '#' || *buf == '\n')
+ return count;
+ p = kstrndup_noeol(buf, 1024);
+ if (!p)
+ return -ENOMEM;
+ hint = snd_array_new(&codec->hints);
+ if (!hint) {
+ kfree(p);
+ return -ENOMEM;
+ }
+ *hint = p;
+ return count;
+}
+
+#define CODEC_ATTR_RW(type) \
+ __ATTR(type, 0644, type##_show, type##_store)
+#define CODEC_ATTR_RO(type) \
+ __ATTR_RO(type)
+#define CODEC_ATTR_WO(type) \
+ __ATTR(type, 0200, NULL, type##_store)
+
+static struct device_attribute codec_attrs[] = {
+ CODEC_ATTR_RW(vendor_id),
+ CODEC_ATTR_RW(subsystem_id),
+ CODEC_ATTR_RW(revision_id),
+ CODEC_ATTR_RO(afg),
+ CODEC_ATTR_RO(mfg),
+ CODEC_ATTR_RW(name),
+ CODEC_ATTR_RW(modelname),
+ CODEC_ATTR_WO(init_verbs),
+ CODEC_ATTR_WO(hints),
+ CODEC_ATTR_WO(reconfig),
+ CODEC_ATTR_WO(clear),
+};
+
+/*
+ * create sysfs files on hwdep directory
+ */
+int snd_hda_hwdep_add_sysfs(struct hda_codec *codec)
+{
+ struct snd_hwdep *hwdep = codec->hwdep;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(codec_attrs); i++)
+ snd_add_device_sysfs_file(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card,
+ hwdep->device, &codec_attrs[i]);
return 0;
}
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index c04ac38e2b2e..f73c13fdd409 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -292,6 +292,8 @@ enum {
/* Define VIA HD Audio Device ID*/
#define VIA_HDAC_DEVICE_ID 0x3288
+/* HD Audio class code */
+#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403
/*
*/
@@ -392,6 +394,7 @@ struct azx {
unsigned int msi :1;
unsigned int irq_pending_warned :1;
unsigned int via_dmapos_patch :1; /* enable DMA-position fix for VIA */
+ unsigned int probing :1; /* codec probing phase */
/* for debugging */
unsigned int last_cmd; /* last issued command (to sync) */
@@ -414,6 +417,7 @@ enum {
AZX_DRIVER_ULI,
AZX_DRIVER_NVIDIA,
AZX_DRIVER_TERA,
+ AZX_DRIVER_GENERIC,
AZX_NUM_DRIVERS, /* keep this as last entry */
};
@@ -427,6 +431,7 @@ static char *driver_short_names[] __devinitdata = {
[AZX_DRIVER_ULI] = "HDA ULI M5461",
[AZX_DRIVER_NVIDIA] = "HDA NVidia",
[AZX_DRIVER_TERA] = "HDA Teradici",
+ [AZX_DRIVER_GENERIC] = "HD-Audio Generic",
};
/*
@@ -527,9 +532,9 @@ static void azx_free_cmd_io(struct azx *chip)
}
/* send a command */
-static int azx_corb_send_cmd(struct hda_codec *codec, u32 val)
+static int azx_corb_send_cmd(struct hda_bus *bus, u32 val)
{
- struct azx *chip = codec->bus->private_data;
+ struct azx *chip = bus->private_data;
unsigned int wp;
/* add command to corb */
@@ -577,9 +582,9 @@ static void azx_update_rirb(struct azx *chip)
}
/* receive a response */
-static unsigned int azx_rirb_get_response(struct hda_codec *codec)
+static unsigned int azx_rirb_get_response(struct hda_bus *bus)
{
- struct azx *chip = codec->bus->private_data;
+ struct azx *chip = bus->private_data;
unsigned long timeout;
again:
@@ -596,7 +601,7 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
}
if (time_after(jiffies, timeout))
break;
- if (codec->bus->needs_damn_long_delay)
+ if (bus->needs_damn_long_delay)
msleep(2); /* temporary workaround */
else {
udelay(10);
@@ -624,6 +629,14 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
goto again;
}
+ if (chip->probing) {
+ /* If this critical timeout happens during the codec probing
+ * phase, this is likely an access to a non-existing codec
+ * slot. Better to return an error and reset the system.
+ */
+ return -1;
+ }
+
snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, "
"switching to single_cmd mode: last cmd=0x%08x\n",
chip->last_cmd);
@@ -646,9 +659,9 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
*/
/* send a command */
-static int azx_single_send_cmd(struct hda_codec *codec, u32 val)
+static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
{
- struct azx *chip = codec->bus->private_data;
+ struct azx *chip = bus->private_data;
int timeout = 50;
while (timeout--) {
@@ -671,9 +684,9 @@ static int azx_single_send_cmd(struct hda_codec *codec, u32 val)
}
/* receive a response */
-static unsigned int azx_single_get_response(struct hda_codec *codec)
+static unsigned int azx_single_get_response(struct hda_bus *bus)
{
- struct azx *chip = codec->bus->private_data;
+ struct azx *chip = bus->private_data;
int timeout = 50;
while (timeout--) {
@@ -696,38 +709,29 @@ static unsigned int azx_single_get_response(struct hda_codec *codec)
*/
/* send a command */
-static int azx_send_cmd(struct hda_codec *codec, hda_nid_t nid,
- int direct, unsigned int verb,
- unsigned int para)
+static int azx_send_cmd(struct hda_bus *bus, unsigned int val)
{
- struct azx *chip = codec->bus->private_data;
- u32 val;
-
- val = (u32)(codec->addr & 0x0f) << 28;
- val |= (u32)direct << 27;
- val |= (u32)nid << 20;
- val |= verb << 8;
- val |= para;
- chip->last_cmd = val;
+ struct azx *chip = bus->private_data;
+ chip->last_cmd = val;
if (chip->single_cmd)
- return azx_single_send_cmd(codec, val);
+ return azx_single_send_cmd(bus, val);
else
- return azx_corb_send_cmd(codec, val);
+ return azx_corb_send_cmd(bus, val);
}
/* get a response */
-static unsigned int azx_get_response(struct hda_codec *codec)
+static unsigned int azx_get_response(struct hda_bus *bus)
{
- struct azx *chip = codec->bus->private_data;
+ struct azx *chip = bus->private_data;
if (chip->single_cmd)
- return azx_single_get_response(codec);
+ return azx_single_get_response(bus);
else
- return azx_rirb_get_response(codec);
+ return azx_rirb_get_response(bus);
}
#ifdef CONFIG_SND_HDA_POWER_SAVE
-static void azx_power_notify(struct hda_codec *codec);
+static void azx_power_notify(struct hda_bus *bus);
#endif
/* reset codec link */
@@ -1184,6 +1188,28 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
return 0;
}
+/*
+ * Probe the given codec address
+ */
+static int probe_codec(struct azx *chip, int addr)
+{
+ unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) |
+ (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID;
+ unsigned int res;
+
+ chip->probing = 1;
+ azx_send_cmd(chip->bus, cmd);
+ res = azx_get_response(chip->bus);
+ chip->probing = 0;
+ if (res == -1)
+ return -EIO;
+ snd_printdd("hda_intel: codec #%d probed OK\n", addr);
+ return 0;
+}
+
+static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
+ struct hda_pcm *cpcm);
+static void azx_stop_chip(struct azx *chip);
/*
* Codec initialization
@@ -1194,21 +1220,12 @@ static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] __devinitdata = {
[AZX_DRIVER_TERA] = 1,
};
-/* number of slots to probe as default
- * this can be different from azx_max_codecs[] -- e.g. some boards
- * report wrongly the non-existing 4th slot availability
- */
-static unsigned int azx_default_codecs[AZX_NUM_DRIVERS] __devinitdata = {
- [AZX_DRIVER_ICH] = 3,
- [AZX_DRIVER_ATI] = 3,
-};
-
static int __devinit azx_codec_create(struct azx *chip, const char *model,
unsigned int codec_probe_mask)
{
struct hda_bus_template bus_temp;
- int c, codecs, audio_codecs, err;
- int def_slots, max_slots;
+ int c, codecs, err;
+ int max_slots;
memset(&bus_temp, 0, sizeof(bus_temp));
bus_temp.private_data = chip;
@@ -1216,6 +1233,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,
bus_temp.pci = chip->pci;
bus_temp.ops.command = azx_send_cmd;
bus_temp.ops.get_response = azx_get_response;
+ bus_temp.ops.attach_pcm = azx_attach_pcm_stream;
#ifdef CONFIG_SND_HDA_POWER_SAVE
bus_temp.ops.pm_notify = azx_power_notify;
#endif
@@ -1227,33 +1245,43 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,
if (chip->driver_type == AZX_DRIVER_NVIDIA)
chip->bus->needs_damn_long_delay = 1;
- codecs = audio_codecs = 0;
+ codecs = 0;
max_slots = azx_max_codecs[chip->driver_type];
if (!max_slots)
max_slots = AZX_MAX_CODECS;
- def_slots = azx_default_codecs[chip->driver_type];
- if (!def_slots)
- def_slots = max_slots;
- for (c = 0; c < def_slots; c++) {
+
+ /* First try to probe all given codec slots */
+ for (c = 0; c < max_slots; c++) {
+ if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
+ if (probe_codec(chip, c) < 0) {
+ /* Some BIOSen give you wrong codec addresses
+ * that don't exist
+ */
+ snd_printk(KERN_WARNING
+ "hda_intel: Codec #%d probe error; "
+ "disabling it...\n", c);
+ chip->codec_mask &= ~(1 << c);
+ /* More badly, accessing to a non-existing
+ * codec often screws up the controller chip,
+ * and distrubs the further communications.
+ * Thus if an error occurs during probing,
+ * better to reset the controller chip to
+ * get back to the sanity state.
+ */
+ azx_stop_chip(chip);
+ azx_init_chip(chip);
+ }
+ }
+ }
+
+ /* Then create codec instances */
+ for (c = 0; c < max_slots; c++) {
if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
struct hda_codec *codec;
err = snd_hda_codec_new(chip->bus, c, &codec);
if (err < 0)
continue;
codecs++;
- if (codec->afg)
- audio_codecs++;
- }
- }
- if (!audio_codecs) {
- /* probe additional slots if no codec is found */
- for (; c < max_slots; c++) {
- if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
- err = snd_hda_codec_new(chip->bus, c, NULL);
- if (err < 0)
- continue;
- codecs++;
- }
}
}
if (!codecs) {
@@ -1722,111 +1750,59 @@ static struct snd_pcm_ops azx_pcm_ops = {
static void azx_pcm_free(struct snd_pcm *pcm)
{
- kfree(pcm->private_data);
+ struct azx_pcm *apcm = pcm->private_data;
+ if (apcm) {
+ apcm->chip->pcm[pcm->device] = NULL;
+ kfree(apcm);
+ }
}
-static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec,
- struct hda_pcm *cpcm)
+static int
+azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
+ struct hda_pcm *cpcm)
{
- int err;
+ struct azx *chip = bus->private_data;
struct snd_pcm *pcm;
struct azx_pcm *apcm;
+ int pcm_dev = cpcm->device;
+ int s, err;
- /* if no substreams are defined for both playback and capture,
- * it's just a placeholder. ignore it.
- */
- if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams)
- return 0;
-
- if (snd_BUG_ON(!cpcm->name))
+ if (pcm_dev >= AZX_MAX_PCMS) {
+ snd_printk(KERN_ERR SFX "Invalid PCM device number %d\n",
+ pcm_dev);
return -EINVAL;
-
- err = snd_pcm_new(chip->card, cpcm->name, cpcm->device,
- cpcm->stream[0].substreams,
- cpcm->stream[1].substreams,
+ }
+ if (chip->pcm[pcm_dev]) {
+ snd_printk(KERN_ERR SFX "PCM %d already exists\n", pcm_dev);
+ return -EBUSY;
+ }
+ err = snd_pcm_new(chip->card, cpcm->name, pcm_dev,
+ cpcm->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams,
+ cpcm->stream[SNDRV_PCM_STREAM_CAPTURE].substreams,
&pcm);
if (err < 0)
return err;
strcpy(pcm->name, cpcm->name);
- apcm = kmalloc(sizeof(*apcm), GFP_KERNEL);
+ apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
if (apcm == NULL)
return -ENOMEM;
apcm->chip = chip;
apcm->codec = codec;
- apcm->hinfo[0] = &cpcm->stream[0];
- apcm->hinfo[1] = &cpcm->stream[1];
pcm->private_data = apcm;
pcm->private_free = azx_pcm_free;
- if (cpcm->stream[0].substreams)
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &azx_pcm_ops);
- if (cpcm->stream[1].substreams)
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &azx_pcm_ops);
+ if (cpcm->pcm_type == HDA_PCM_TYPE_MODEM)
+ pcm->dev_class = SNDRV_PCM_CLASS_MODEM;
+ chip->pcm[pcm_dev] = pcm;
+ cpcm->pcm = pcm;
+ for (s = 0; s < 2; s++) {
+ apcm->hinfo[s] = &cpcm->stream[s];
+ if (cpcm->stream[s].substreams)
+ snd_pcm_set_ops(pcm, s, &azx_pcm_ops);
+ }
+ /* buffer pre-allocation */
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
snd_dma_pci_data(chip->pci),
1024 * 64, 32 * 1024 * 1024);
- chip->pcm[cpcm->device] = pcm;
- return 0;
-}
-
-static int __devinit azx_pcm_create(struct azx *chip)
-{
- static const char *dev_name[HDA_PCM_NTYPES] = {
- "Audio", "SPDIF", "HDMI", "Modem"
- };
- /* starting device index for each PCM type */
- static int dev_idx[HDA_PCM_NTYPES] = {
- [HDA_PCM_TYPE_AUDIO] = 0,
- [HDA_PCM_TYPE_SPDIF] = 1,
- [HDA_PCM_TYPE_HDMI] = 3,
- [HDA_PCM_TYPE_MODEM] = 6
- };
- /* normal audio device indices; not linear to keep compatibility */
- static int audio_idx[4] = { 0, 2, 4, 5 };
- struct hda_codec *codec;
- int c, err;
- int num_devs[HDA_PCM_NTYPES];
-
- err = snd_hda_build_pcms(chip->bus);
- if (err < 0)
- return err;
-
- /* create audio PCMs */
- memset(num_devs, 0, sizeof(num_devs));
- list_for_each_entry(codec, &chip->bus->codec_list, list) {
- for (c = 0; c < codec->num_pcms; c++) {
- struct hda_pcm *cpcm = &codec->pcm_info[c];
- int type = cpcm->pcm_type;
- switch (type) {
- case HDA_PCM_TYPE_AUDIO:
- if (num_devs[type] >= ARRAY_SIZE(audio_idx)) {
- snd_printk(KERN_WARNING
- "Too many audio devices\n");
- continue;
- }
- cpcm->device = audio_idx[num_devs[type]];
- break;
- case HDA_PCM_TYPE_SPDIF:
- case HDA_PCM_TYPE_HDMI:
- case HDA_PCM_TYPE_MODEM:
- if (num_devs[type]) {
- snd_printk(KERN_WARNING
- "%s already defined\n",
- dev_name[type]);
- continue;
- }
- cpcm->device = dev_idx[type];
- break;
- default:
- snd_printk(KERN_WARNING
- "Invalid PCM type %d\n", type);
- continue;
- }
- num_devs[type]++;
- err = create_codec_pcm(chip, codec, cpcm);
- if (err < 0)
- return err;
- }
- }
return 0;
}
@@ -1903,13 +1879,13 @@ static void azx_stop_chip(struct azx *chip)
#ifdef CONFIG_SND_HDA_POWER_SAVE
/* power-up/down the controller */
-static void azx_power_notify(struct hda_codec *codec)
+static void azx_power_notify(struct hda_bus *bus)
{
- struct azx *chip = codec->bus->private_data;
+ struct azx *chip = bus->private_data;
struct hda_codec *c;
int power_on = 0;
- list_for_each_entry(c, &codec->bus->codec_list, list) {
+ list_for_each_entry(c, &bus->codec_list, list) {
if (c->power_on) {
power_on = 1;
break;
@@ -2095,6 +2071,8 @@ static struct snd_pci_quirk probe_mask_list[] __devinitdata = {
SND_PCI_QUIRK(0x1014, 0x05b7, "Thinkpad Z60", 0x01),
SND_PCI_QUIRK(0x17aa, 0x2010, "Thinkpad X/T/R60", 0x01),
SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X/T/R61", 0x01),
+ /* broken BIOS */
+ SND_PCI_QUIRK(0x1028, 0x20ac, "Dell Studio Desktop", 0x01),
{}
};
@@ -2229,6 +2207,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
chip->playback_streams = ATIHDMI_NUM_PLAYBACK;
chip->capture_streams = ATIHDMI_NUM_CAPTURE;
break;
+ case AZX_DRIVER_GENERIC:
default:
chip->playback_streams = ICH6_NUM_PLAYBACK;
chip->capture_streams = ICH6_NUM_CAPTURE;
@@ -2352,7 +2331,7 @@ static int __devinit azx_probe(struct pci_dev *pci,
}
/* create PCM streams */
- err = azx_pcm_create(chip);
+ err = snd_hda_build_pcms(chip->bus);
if (err < 0) {
snd_card_free(card);
return err;
@@ -2453,6 +2432,11 @@ static struct pci_device_id azx_ids[] = {
{ PCI_DEVICE(0x10de, 0x0bd7), .driver_data = AZX_DRIVER_NVIDIA },
/* Teradici */
{ PCI_DEVICE(0x6549, 0x1200), .driver_data = AZX_DRIVER_TERA },
+ /* AMD Generic, PCI class code and Vendor ID for HD Audio */
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID),
+ .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
+ .class_mask = 0xffffff,
+ .driver_data = AZX_DRIVER_GENERIC },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, azx_ids);
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 7957fefda730..02ac7321e5e4 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -96,6 +96,8 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
const char *name);
int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
unsigned int *tlv, const char **slaves);
+void snd_hda_codec_reset(struct hda_codec *codec);
+int snd_hda_codec_configure(struct hda_codec *codec);
/* amp value bits */
#define HDA_AMP_MUTE 0x80
@@ -282,6 +284,9 @@ int snd_hda_codec_proc_new(struct hda_codec *codec);
static inline int snd_hda_codec_proc_new(struct hda_codec *codec) { return 0; }
#endif
+#define SND_PRINT_RATES_ADVISED_BUFSIZE 80
+void snd_print_pcm_rates(int pcm, char *buf, int buflen);
+
/*
* Misc
*/
@@ -364,17 +369,17 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
/* amp values */
#define AMP_IN_MUTE(idx) (0x7080 | ((idx)<<8))
#define AMP_IN_UNMUTE(idx) (0x7000 | ((idx)<<8))
-#define AMP_OUT_MUTE 0xb080
-#define AMP_OUT_UNMUTE 0xb000
-#define AMP_OUT_ZERO 0xb000
+#define AMP_OUT_MUTE 0xb080
+#define AMP_OUT_UNMUTE 0xb000
+#define AMP_OUT_ZERO 0xb000
/* pinctl values */
#define PIN_IN (AC_PINCTL_IN_EN)
-#define PIN_VREFHIZ (AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ)
+#define PIN_VREFHIZ (AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ)
#define PIN_VREF50 (AC_PINCTL_IN_EN | AC_PINCTL_VREF_50)
-#define PIN_VREFGRD (AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD)
+#define PIN_VREFGRD (AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD)
#define PIN_VREF80 (AC_PINCTL_IN_EN | AC_PINCTL_VREF_80)
-#define PIN_VREF100 (AC_PINCTL_IN_EN | AC_PINCTL_VREF_100)
-#define PIN_OUT (AC_PINCTL_OUT_EN)
+#define PIN_VREF100 (AC_PINCTL_IN_EN | AC_PINCTL_VREF_100)
+#define PIN_OUT (AC_PINCTL_OUT_EN)
#define PIN_HP (AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN)
#define PIN_HP_AMP (AC_PINCTL_HP_EN)
@@ -393,10 +398,18 @@ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction);
int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int caps);
+int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl);
+void snd_hda_ctls_clear(struct hda_codec *codec);
+
/*
* hwdep interface
*/
+#ifdef CONFIG_SND_HDA_HWDEP
int snd_hda_create_hwdep(struct hda_codec *codec);
+int snd_hda_hwdep_add_sysfs(struct hda_codec *codec);
+#else
+static inline int snd_hda_create_hwdep(struct hda_codec *codec) { return 0; }
+#endif
/*
* power-management
@@ -430,4 +443,54 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
#define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1)
#define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf)
+/*
+ * CEA Short Audio Descriptor data
+ */
+struct cea_sad {
+ int channels;
+ int format; /* (format == 0) indicates invalid SAD */
+ int rates;
+ int sample_bits; /* for LPCM */
+ int max_bitrate; /* for AC3...ATRAC */
+ int profile; /* for WMAPRO */
+};
+
+#define ELD_FIXED_BYTES 20
+#define ELD_MAX_MNL 16
+#define ELD_MAX_SAD 16
+
+/*
+ * ELD: EDID Like Data
+ */
+struct sink_eld {
+ int eld_size;
+ int baseline_len;
+ int eld_ver; /* (eld_ver == 0) indicates invalid ELD */
+ int cea_edid_ver;
+ char monitor_name[ELD_MAX_MNL + 1];
+ int manufacture_id;
+ int product_id;
+ u64 port_id;
+ int support_hdcp;
+ int support_ai;
+ int conn_type;
+ int aud_synch_delay;
+ int spk_alloc;
+ int sad_count;
+ struct cea_sad sad[ELD_MAX_SAD];
+};
+
+int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid);
+int snd_hdmi_get_eld(struct sink_eld *, struct hda_codec *, hda_nid_t);
+void snd_hdmi_show_eld(struct sink_eld *eld);
+
+#ifdef CONFIG_PROC_FS
+int snd_hda_eld_proc_new(struct hda_codec *codec, struct sink_eld *eld);
+#else
+inline int snd_hda_eld_proc_new(struct hda_codec *codec, struct sink_eld *eld)
+{
+ return 0;
+}
+#endif
+
#endif /* __SOUND_HDA_LOCAL_H */
diff --git a/sound/pci/hda/hda_patch.h b/sound/pci/hda/hda_patch.h
index dfbcfa88da44..38212c1020a6 100644
--- a/sound/pci/hda/hda_patch.h
+++ b/sound/pci/hda/hda_patch.h
@@ -20,3 +20,5 @@ extern struct hda_codec_preset snd_hda_preset_conexant[];
extern struct hda_codec_preset snd_hda_preset_via[];
/* NVIDIA HDMI codecs */
extern struct hda_codec_preset snd_hda_preset_nvhdmi[];
+/* INTEL HDMI codecs */
+extern struct hda_codec_preset snd_hda_preset_intelhdmi[];
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
index c39af986bff1..512eb674b743 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
@@ -89,20 +89,28 @@ static void print_amp_vals(struct snd_info_buffer *buffer,
snd_iprintf(buffer, "\n");
}
-static void print_pcm_rates(struct snd_info_buffer *buffer, unsigned int pcm)
+void snd_print_pcm_rates(int pcm, char *buf, int buflen)
{
static unsigned int rates[] = {
8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200,
96000, 176400, 192000, 384000
};
- int i;
+ int i, j;
+
+ for (i = 0, j = 0; i < ARRAY_SIZE(rates); i++)
+ if (pcm & (1 << i))
+ j += snprintf(buf + j, buflen - j, " %d", rates[i]);
+
+ buf[j] = '\0'; /* necessary when j == 0 */
+}
+static void print_pcm_rates(struct snd_info_buffer *buffer, unsigned int pcm)
+{
+ char buf[SND_PRINT_RATES_ADVISED_BUFSIZE];
pcm &= AC_SUPPCM_RATES;
snd_iprintf(buffer, " rates [0x%x]:", pcm);
- for (i = 0; i < ARRAY_SIZE(rates); i++)
- if (pcm & (1 << i))
- snd_iprintf(buffer, " %d", rates[i]);
- snd_iprintf(buffer, "\n");
+ snd_print_pcm_rates(pcm, buf, sizeof(buf));
+ snd_iprintf(buffer, "%s\n", buf);
}
static void print_pcm_bits(struct snd_info_buffer *buffer, unsigned int pcm)
@@ -145,32 +153,6 @@ static void print_pcm_caps(struct snd_info_buffer *buffer,
print_pcm_formats(buffer, stream);
}
-static const char *get_jack_location(u32 cfg)
-{
- static char *bases[7] = {
- "N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom",
- };
- static unsigned char specials_idx[] = {
- 0x07, 0x08,
- 0x17, 0x18, 0x19,
- 0x37, 0x38
- };
- static char *specials[] = {
- "Rear Panel", "Drive Bar",
- "Riser", "HDMI", "ATAPI",
- "Mobile-In", "Mobile-Out"
- };
- int i;
- cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT;
- if ((cfg & 0x0f) < 7)
- return bases[cfg & 0x0f];
- for (i = 0; i < ARRAY_SIZE(specials_idx); i++) {
- if (cfg == specials_idx[i])
- return specials[i];
- }
- return "UNKNOWN";
-}
-
static const char *get_jack_connection(u32 cfg)
{
static char *names[16] = {
@@ -206,13 +188,6 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
int *supports_vref)
{
static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" };
- static char *jack_types[16] = {
- "Line Out", "Speaker", "HP Out", "CD",
- "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand",
- "Line In", "Aux", "Mic", "Telephony",
- "SPDIF In", "Digitial In", "Reserved", "Other"
- };
- static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" };
unsigned int caps, val;
caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
@@ -274,9 +249,9 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
snd_iprintf(buffer, " Pin Default 0x%08x: [%s] %s at %s %s\n", caps,
jack_conns[(caps & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT],
- jack_types[(caps & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT],
- jack_locations[(caps >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3],
- get_jack_location(caps));
+ snd_hda_get_jack_type(caps),
+ snd_hda_get_jack_connectivity(caps),
+ snd_hda_get_jack_location(caps));
snd_iprintf(buffer, " Conn = %s, Color = %s\n",
get_jack_connection(caps),
get_jack_color(caps));
@@ -513,12 +488,11 @@ static void print_codec_info(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct hda_codec *codec = entry->private_data;
- char buf[32];
hda_nid_t nid;
int i, nodes;
- snd_hda_get_codec_name(codec, buf, sizeof(buf));
- snd_iprintf(buffer, "Codec: %s\n", buf);
+ snd_iprintf(buffer, "Codec: %s\n",
+ codec->name ? codec->name : "Not Set");
snd_iprintf(buffer, "Address: %d\n", codec->addr);
snd_iprintf(buffer, "Vendor Id: 0x%x\n", codec->vendor_id);
snd_iprintf(buffer, "Subsystem Id: 0x%x\n", codec->subsystem_id);
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index 686c77491dea..032cbb4bd985 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -67,8 +67,7 @@ struct ad198x_spec {
/* dynamic controls, init_verbs and input_mux */
struct auto_pin_cfg autocfg;
- unsigned int num_kctl_alloc, num_kctl_used;
- struct snd_kcontrol_new *kctl_alloc;
+ struct snd_array kctls;
struct hda_input_mux private_imux;
hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
@@ -154,6 +153,8 @@ static const char *ad_slave_sws[] = {
NULL
};
+static void ad198x_free_kctls(struct hda_codec *codec);
+
static int ad198x_build_controls(struct hda_codec *codec)
{
struct ad198x_spec *spec = codec->spec;
@@ -202,6 +203,7 @@ static int ad198x_build_controls(struct hda_codec *codec)
return err;
}
+ ad198x_free_kctls(codec); /* no longer needed */
return 0;
}
@@ -375,16 +377,27 @@ static int ad198x_build_pcms(struct hda_codec *codec)
return 0;
}
-static void ad198x_free(struct hda_codec *codec)
+static void ad198x_free_kctls(struct hda_codec *codec)
{
struct ad198x_spec *spec = codec->spec;
- unsigned int i;
- if (spec->kctl_alloc) {
- for (i = 0; i < spec->num_kctl_used; i++)
- kfree(spec->kctl_alloc[i].name);
- kfree(spec->kctl_alloc);
+ if (spec->kctls.list) {
+ struct snd_kcontrol_new *kctl = spec->kctls.list;
+ int i;
+ for (i = 0; i < spec->kctls.used; i++)
+ kfree(kctl[i].name);
}
+ snd_array_free(&spec->kctls);
+}
+
+static void ad198x_free(struct hda_codec *codec)
+{
+ struct ad198x_spec *spec = codec->spec;
+
+ if (!spec)
+ return;
+
+ ad198x_free_kctls(codec);
kfree(codec->spec);
}
@@ -2452,9 +2465,6 @@ static struct hda_amp_list ad1988_loopbacks[] = {
* Automatic parse of I/O pins from the BIOS configuration
*/
-#define NUM_CONTROL_ALLOC 32
-#define NUM_VERB_ALLOC 32
-
enum {
AD_CTL_WIDGET_VOL,
AD_CTL_WIDGET_MUTE,
@@ -2472,27 +2482,15 @@ static int add_control(struct ad198x_spec *spec, int type, const char *name,
{
struct snd_kcontrol_new *knew;
- if (spec->num_kctl_used >= spec->num_kctl_alloc) {
- int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
-
- knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */
- if (! knew)
- return -ENOMEM;
- if (spec->kctl_alloc) {
- memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc);
- kfree(spec->kctl_alloc);
- }
- spec->kctl_alloc = knew;
- spec->num_kctl_alloc = num;
- }
-
- knew = &spec->kctl_alloc[spec->num_kctl_used];
+ snd_array_init(&spec->kctls, sizeof(*knew), 32);
+ knew = snd_array_new(&spec->kctls);
+ if (!knew)
+ return -ENOMEM;
*knew = ad1988_control_templates[type];
knew->name = kstrdup(name, GFP_KERNEL);
if (! knew->name)
return -ENOMEM;
knew->private_value = val;
- spec->num_kctl_used++;
return 0;
}
@@ -2846,8 +2844,8 @@ static int ad1988_parse_auto_config(struct hda_codec *codec)
if (spec->autocfg.dig_in_pin)
spec->dig_in_nid = AD1988_SPDIF_IN;
- if (spec->kctl_alloc)
- spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->kctls.list)
+ spec->mixers[spec->num_mixers++] = spec->kctls.list;
spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
diff --git a/sound/pci/hda/patch_atihdmi.c b/sound/pci/hda/patch_atihdmi.c
index ba61575983fd..5603a1acddb1 100644
--- a/sound/pci/hda/patch_atihdmi.c
+++ b/sound/pci/hda/patch_atihdmi.c
@@ -193,7 +193,6 @@ struct hda_codec_preset snd_hda_preset_atihdmi[] = {
{ .id = 0x1002791a, .name = "ATI RS690/780 HDMI", .patch = patch_atihdmi },
{ .id = 0x1002aa01, .name = "ATI R6xx HDMI", .patch = patch_atihdmi },
{ .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_atihdmi },
- { .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_atihdmi },
{ .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_atihdmi },
{} /* terminator */
};
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 7c1eb23f0cec..076010708152 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -86,8 +86,6 @@ struct conexant_spec {
/* dynamic controls, init_verbs and input_mux */
struct auto_pin_cfg autocfg;
- unsigned int num_kctl_alloc, num_kctl_used;
- struct snd_kcontrol_new *kctl_alloc;
struct hda_input_mux private_imux;
hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
@@ -344,15 +342,6 @@ static int conexant_init(struct hda_codec *codec)
static void conexant_free(struct hda_codec *codec)
{
- struct conexant_spec *spec = codec->spec;
- unsigned int i;
-
- if (spec->kctl_alloc) {
- for (i = 0; i < spec->num_kctl_used; i++)
- kfree(spec->kctl_alloc[i].name);
- kfree(spec->kctl_alloc);
- }
-
kfree(codec->spec);
}
diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c
new file mode 100644
index 000000000000..c95abc47614f
--- /dev/null
+++ b/sound/pci/hda/patch_intelhdmi.c
@@ -0,0 +1,461 @@
+/*
+ *
+ * patch_intelhdmi.c - Patch for Intel HDMI codecs
+ *
+ * Copyright(c) 2008 Intel Corporation. All rights reserved.
+ *
+ * Authors:
+ * Jiang Zhe <zhe.jiang@intel.com>
+ * Wu Fengguang <wfg@linux.intel.com>
+ *
+ * Maintained by:
+ * Wu Fengguang <wfg@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+#include "hda_patch.h"
+
+#define CVT_NID 0x02 /* audio converter */
+#define PIN_NID 0x03 /* HDMI output pin */
+
+#define INTEL_HDMI_EVENT_TAG 0x08
+
+struct intel_hdmi_spec {
+ struct hda_multi_out multiout;
+ struct hda_pcm pcm_rec;
+ struct sink_eld sink;
+};
+
+static struct hda_verb pinout_enable_verb[] = {
+ {PIN_NID, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {} /* terminator */
+};
+
+static struct hda_verb pinout_disable_verb[] = {
+ {PIN_NID, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00},
+ {}
+};
+
+static struct hda_verb unsolicited_response_verb[] = {
+ {PIN_NID, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN |
+ INTEL_HDMI_EVENT_TAG},
+ {}
+};
+
+static struct hda_verb def_chan_map[] = {
+ {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x00},
+ {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x11},
+ {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x22},
+ {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x33},
+ {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x44},
+ {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x55},
+ {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x66},
+ {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x77},
+ {}
+};
+
+
+struct hdmi_audio_infoframe {
+ u8 type; /* 0x84 */
+ u8 ver; /* 0x01 */
+ u8 len; /* 0x0a */
+
+ u8 checksum; /* PB0 */
+ u8 CC02_CT47; /* CC in bits 0:2, CT in 4:7 */
+ u8 SS01_SF24;
+ u8 CXT04;
+ u8 CA;
+ u8 LFEPBL01_LSV36_DM_INH7;
+ u8 reserved[5]; /* PB6 - PB10 */
+};
+
+/*
+ * HDMI routines
+ */
+
+#ifdef BE_PARANOID
+static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t nid,
+ int *packet_index, int *byte_index)
+{
+ int val;
+
+ val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_INDEX, 0);
+
+ *packet_index = val >> 5;
+ *byte_index = val & 0x1f;
+}
+#endif
+
+static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t nid,
+ int packet_index, int byte_index)
+{
+ int val;
+
+ val = (packet_index << 5) | (byte_index & 0x1f);
+
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val);
+}
+
+static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t nid,
+ unsigned char val)
+{
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val);
+}
+
+static void hdmi_enable_output(struct hda_codec *codec)
+{
+ /* Enable Audio InfoFrame Transmission */
+ hdmi_set_dip_index(codec, PIN_NID, 0x0, 0x0);
+ snd_hda_codec_write(codec, PIN_NID, 0, AC_VERB_SET_HDMI_DIP_XMIT,
+ AC_DIPXMIT_BEST);
+ /* Unmute */
+ if (get_wcaps(codec, PIN_NID) & AC_WCAP_OUT_AMP)
+ snd_hda_codec_write(codec, PIN_NID, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+ /* Enable pin out */
+ snd_hda_sequence_write(codec, pinout_enable_verb);
+}
+
+static void hdmi_disable_output(struct hda_codec *codec)
+{
+ snd_hda_sequence_write(codec, pinout_disable_verb);
+ if (get_wcaps(codec, PIN_NID) & AC_WCAP_OUT_AMP)
+ snd_hda_codec_write(codec, PIN_NID, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+ /*
+ * FIXME: noises may arise when playing music after reloading the
+ * kernel module, until the next X restart or monitor repower.
+ */
+}
+
+static int hdmi_get_channel_count(struct hda_codec *codec)
+{
+ return 1 + snd_hda_codec_read(codec, CVT_NID, 0,
+ AC_VERB_GET_CVT_CHAN_COUNT, 0);
+}
+
+static void hdmi_set_channel_count(struct hda_codec *codec, int chs)
+{
+ snd_hda_codec_write(codec, CVT_NID, 0,
+ AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
+
+ if (chs != hdmi_get_channel_count(codec))
+ snd_printd(KERN_INFO "Channel count expect=%d, real=%d\n",
+ chs, hdmi_get_channel_count(codec));
+}
+
+static void hdmi_debug_slot_mapping(struct hda_codec *codec)
+{
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ int i;
+ int slot;
+
+ for (i = 0; i < 8; i++) {
+ slot = snd_hda_codec_read(codec, CVT_NID, 0,
+ AC_VERB_GET_HDMI_CHAN_SLOT, i);
+ printk(KERN_DEBUG "ASP channel %d => slot %d\n",
+ slot >> 4, slot & 0x7);
+ }
+#endif
+}
+
+static void hdmi_setup_channel_mapping(struct hda_codec *codec)
+{
+ snd_hda_sequence_write(codec, def_chan_map);
+ hdmi_debug_slot_mapping(codec);
+}
+
+
+static void hdmi_parse_eld(struct hda_codec *codec)
+{
+ struct intel_hdmi_spec *spec = codec->spec;
+ struct sink_eld *eld = &spec->sink;
+
+ if (!snd_hdmi_get_eld(eld, codec, PIN_NID))
+ snd_hdmi_show_eld(eld);
+}
+
+
+/*
+ * Audio Infoframe routines
+ */
+
+static void hdmi_debug_dip_size(struct hda_codec *codec)
+{
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ int i;
+ int size;
+
+ size = snd_hdmi_get_eld_size(codec, PIN_NID);
+ printk(KERN_DEBUG "ELD buf size is %d\n", size);
+
+ for (i = 0; i < 8; i++) {
+ size = snd_hda_codec_read(codec, PIN_NID, 0,
+ AC_VERB_GET_HDMI_DIP_SIZE, i);
+ printk(KERN_DEBUG "DIP GP[%d] buf size is %d\n", i, size);
+ }
+#endif
+}
+
+static void hdmi_clear_dip_buffers(struct hda_codec *codec)
+{
+#ifdef BE_PARANOID
+ int i, j;
+ int size;
+ int pi, bi;
+ for (i = 0; i < 8; i++) {
+ size = snd_hda_codec_read(codec, PIN_NID, 0,
+ AC_VERB_GET_HDMI_DIP_SIZE, i);
+ if (size == 0)
+ continue;
+
+ hdmi_set_dip_index(codec, PIN_NID, i, 0x0);
+ for (j = 1; j < 1000; j++) {
+ hdmi_write_dip_byte(codec, PIN_NID, 0x0);
+ hdmi_get_dip_index(codec, PIN_NID, &pi, &bi);
+ if (pi != i)
+ snd_printd(KERN_INFO "dip index %d: %d != %d\n",
+ bi, pi, i);
+ if (bi == 0) /* byte index wrapped around */
+ break;
+ }
+ snd_printd(KERN_INFO
+ "DIP GP[%d] buf reported size=%d, written=%d\n",
+ i, size, j);
+ }
+#endif
+}
+
+static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hdmi_audio_infoframe audio_infoframe = {
+ .type = 0x84,
+ .ver = 0x01,
+ .len = 0x0a,
+ .CC02_CT47 = substream->runtime->channels - 1,
+ };
+ u8 *params = (u8 *)&audio_infoframe;
+ int i;
+
+ hdmi_debug_dip_size(codec);
+ hdmi_clear_dip_buffers(codec); /* be paranoid */
+
+ hdmi_set_dip_index(codec, PIN_NID, 0x0, 0x0);
+ for (i = 0; i < sizeof(audio_infoframe); i++)
+ hdmi_write_dip_byte(codec, PIN_NID, params[i]);
+}
+
+
+/*
+ * Unsolicited events
+ */
+
+static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
+{
+ int pind = !!(res & AC_UNSOL_RES_PD);
+ int eldv = !!(res & AC_UNSOL_RES_ELDV);
+
+ printk(KERN_INFO "HDMI intrinsic event: PD=%d ELDV=%d\n", pind, eldv);
+
+ if (pind && eldv) {
+ hdmi_parse_eld(codec);
+ /* TODO: do real things about ELD */
+ }
+}
+
+static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
+{
+ int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
+ int cp_state = !!(res & AC_UNSOL_RES_CP_STATE);
+ int cp_ready = !!(res & AC_UNSOL_RES_CP_READY);
+
+ printk(KERN_INFO "HDMI non-intrinsic event: "
+ "SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
+ subtag,
+ cp_state,
+ cp_ready);
+
+ /* who cares? */
+ if (cp_state)
+ ;
+ if (cp_ready)
+ ;
+}
+
+
+static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
+ int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
+
+ if (tag != INTEL_HDMI_EVENT_TAG) {
+ snd_printd(KERN_INFO
+ "Unexpected HDMI unsolicited event tag 0x%x\n",
+ tag);
+ return;
+ }
+
+ if (subtag == 0)
+ hdmi_intrinsic_event(codec, res);
+ else
+ hdmi_non_intrinsic_event(codec, res);
+}
+
+/*
+ * Callbacks
+ */
+
+static int intel_hdmi_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct intel_hdmi_spec *spec = codec->spec;
+
+ return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int intel_hdmi_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct intel_hdmi_spec *spec = codec->spec;
+
+ hdmi_disable_output(codec);
+
+ return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct intel_hdmi_spec *spec = codec->spec;
+
+ snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
+ format, substream);
+
+ hdmi_set_channel_count(codec, substream->runtime->channels);
+
+ /* wfg: channel mapping not supported by DEVCTG */
+ hdmi_setup_channel_mapping(codec);
+
+ hdmi_setup_audio_infoframe(codec, substream);
+
+ hdmi_enable_output(codec);
+
+ return 0;
+}
+
+static struct hda_pcm_stream intel_hdmi_pcm_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 8,
+ .nid = CVT_NID, /* NID to query formats and rates and setup streams */
+ .ops = {
+ .open = intel_hdmi_playback_pcm_open,
+ .close = intel_hdmi_playback_pcm_close,
+ .prepare = intel_hdmi_playback_pcm_prepare
+ },
+};
+
+static int intel_hdmi_build_pcms(struct hda_codec *codec)
+{
+ struct intel_hdmi_spec *spec = codec->spec;
+ struct hda_pcm *info = &spec->pcm_rec;
+
+ codec->num_pcms = 1;
+ codec->pcm_info = info;
+
+ info->name = "INTEL HDMI";
+ info->pcm_type = HDA_PCM_TYPE_HDMI;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = intel_hdmi_pcm_playback;
+
+ return 0;
+}
+
+static int intel_hdmi_build_controls(struct hda_codec *codec)
+{
+ struct intel_hdmi_spec *spec = codec->spec;
+ int err;
+
+ err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int intel_hdmi_init(struct hda_codec *codec)
+{
+ /* disable audio output as early as possible */
+ hdmi_disable_output(codec);
+
+ snd_hda_sequence_write(codec, unsolicited_response_verb);
+
+ return 0;
+}
+
+static void intel_hdmi_free(struct hda_codec *codec)
+{
+ kfree(codec->spec);
+}
+
+static struct hda_codec_ops intel_hdmi_patch_ops = {
+ .init = intel_hdmi_init,
+ .free = intel_hdmi_free,
+ .build_pcms = intel_hdmi_build_pcms,
+ .build_controls = intel_hdmi_build_controls,
+ .unsol_event = intel_hdmi_unsol_event,
+};
+
+static int patch_intel_hdmi(struct hda_codec *codec)
+{
+ struct intel_hdmi_spec *spec;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ spec->multiout.num_dacs = 0; /* no analog */
+ spec->multiout.max_channels = 8;
+ spec->multiout.dig_out_nid = CVT_NID;
+
+ codec->spec = spec;
+ codec->patch_ops = intel_hdmi_patch_ops;
+
+ snd_hda_eld_proc_new(codec, &spec->sink);
+
+ return 0;
+}
+
+struct hda_codec_preset snd_hda_preset_intelhdmi[] = {
+ { .id = 0x808629fb, .name = "INTEL G45 DEVCL", .patch = patch_intel_hdmi },
+ { .id = 0x80862801, .name = "INTEL G45 DEVBLC", .patch = patch_intel_hdmi },
+ { .id = 0x80862802, .name = "INTEL G45 DEVCTG", .patch = patch_intel_hdmi },
+ { .id = 0x80862803, .name = "INTEL G45 DEVELK", .patch = patch_intel_hdmi },
+ { .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_intel_hdmi },
+ {} /* terminator */
+};
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index a378c0145125..04e153a77dbc 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -114,6 +114,7 @@ enum {
ALC268_3ST,
ALC268_TOSHIBA,
ALC268_ACER,
+ ALC268_ACER_DMIC,
ALC268_ACER_ASPIRE_ONE,
ALC268_DELL,
ALC268_ZEPTO,
@@ -130,6 +131,8 @@ enum {
ALC269_QUANTA_FL1,
ALC269_ASUS_EEEPC_P703,
ALC269_ASUS_EEEPC_P901,
+ ALC269_FUJITSU,
+ ALC269_LIFEBOOK,
ALC269_AUTO,
ALC269_MODEL_LAST /* last tag */
};
@@ -152,6 +155,7 @@ enum {
enum {
ALC660VD_3ST,
ALC660VD_3ST_DIG,
+ ALC660VD_ASUS_V1S,
ALC861VD_3ST,
ALC861VD_3ST_DIG,
ALC861VD_6ST_DIG,
@@ -228,6 +232,7 @@ enum {
ALC883_3ST_6ch_INTEL,
ALC888_ASUS_M90V,
ALC888_ASUS_EEE1601,
+ ALC1200_ASUS_P5Q,
ALC883_AUTO,
ALC883_MODEL_LAST,
};
@@ -239,6 +244,7 @@ struct alc_spec {
/* codec parameterization */
struct snd_kcontrol_new *mixers[5]; /* mixer arrays */
unsigned int num_mixers;
+ struct snd_kcontrol_new *cap_mixer; /* capture mixer */
const struct hda_verb *init_verbs[5]; /* initialization verbs
* don't forget NULL
@@ -268,6 +274,7 @@ struct alc_spec {
hda_nid_t *adc_nids;
hda_nid_t *capsrc_nids;
hda_nid_t dig_in_nid; /* digital-in NID; optional */
+ unsigned char is_mix_capture; /* matrix-style capture (non-mux) */
/* capture source */
unsigned int num_mux_defs;
@@ -284,8 +291,7 @@ struct alc_spec {
/* dynamic controls, init_verbs and input_mux */
struct auto_pin_cfg autocfg;
- unsigned int num_kctl_alloc, num_kctl_used;
- struct snd_kcontrol_new *kctl_alloc;
+ struct snd_array kctls;
struct hda_input_mux private_imux;
hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
@@ -323,6 +329,7 @@ struct alc_config_preset {
struct snd_kcontrol_new *mixers[5]; /* should be identical size
* with spec
*/
+ struct snd_kcontrol_new *cap_mixer; /* capture mixer */
const struct hda_verb *init_verbs[5];
unsigned int num_dacs;
hda_nid_t *dac_nids;
@@ -375,14 +382,38 @@ static int alc_mux_enum_put(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec;
+ const struct hda_input_mux *imux = spec->input_mux;
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]);
-}
+ if (spec->is_mix_capture) {
+ /* Matrix-mixer style (e.g. ALC882) */
+ unsigned int *cur_val = &spec->cur_mux[adc_idx];
+ unsigned int i, idx;
+
+ idx = ucontrol->value.enumerated.item[0];
+ if (idx >= imux->num_items)
+ idx = imux->num_items - 1;
+ if (*cur_val == idx)
+ return 0;
+ for (i = 0; i < imux->num_items; i++) {
+ unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE;
+ snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT,
+ imux->items[i].index,
+ HDA_AMP_MUTE, v);
+ }
+ *cur_val = idx;
+ return 1;
+ } else {
+ /* MUX style (e.g. ALC880) */
+ unsigned int mux_idx;
+ mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx;
+ return snd_hda_input_mux_put(codec, &spec->input_mux[mux_idx],
+ ucontrol, nid,
+ &spec->cur_mux[adc_idx]);
+ }
+}
/*
* channel mode setting
@@ -717,6 +748,22 @@ static int alc_eapd_ctrl_put(struct snd_kcontrol *kcontrol,
#endif /* CONFIG_SND_DEBUG */
/*
+ */
+static void add_mixer(struct alc_spec *spec, struct snd_kcontrol_new *mix)
+{
+ if (snd_BUG_ON(spec->num_mixers >= ARRAY_SIZE(spec->mixers)))
+ return;
+ spec->mixers[spec->num_mixers++] = mix;
+}
+
+static void add_verb(struct alc_spec *spec, const struct hda_verb *verb)
+{
+ if (snd_BUG_ON(spec->num_init_verbs >= ARRAY_SIZE(spec->init_verbs)))
+ return;
+ spec->init_verbs[spec->num_init_verbs++] = verb;
+}
+
+/*
* set up from the preset table
*/
static void setup_preset(struct alc_spec *spec,
@@ -725,11 +772,11 @@ static void setup_preset(struct alc_spec *spec,
int i;
for (i = 0; i < ARRAY_SIZE(preset->mixers) && preset->mixers[i]; i++)
- spec->mixers[spec->num_mixers++] = preset->mixers[i];
+ add_mixer(spec, preset->mixers[i]);
+ spec->cap_mixer = preset->cap_mixer;
for (i = 0; i < ARRAY_SIZE(preset->init_verbs) && preset->init_verbs[i];
i++)
- spec->init_verbs[spec->num_init_verbs++] =
- preset->init_verbs[i];
+ add_verb(spec, preset->init_verbs[i]);
spec->channel_mode = preset->channel_mode;
spec->num_channel_mode = preset->num_channel_mode;
@@ -1205,49 +1252,126 @@ static struct snd_kcontrol_new alc880_three_stack_mixer[] = {
};
/* capture mixer elements */
-static struct snd_kcontrol_new alc880_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 3,
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
- { } /* end */
-};
+static int alc_cap_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+ int err;
-/* capture mixer elements (in case NID 0x07 not available) */
-static struct snd_kcontrol_new alc880_capture_alt_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
- { } /* end */
-};
+ mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0,
+ HDA_INPUT);
+ err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo);
+ mutex_unlock(&codec->spdif_mutex); /* reuse spdif_mutex */
+ return err;
+}
+
+static int alc_cap_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *tlv)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+ int err;
+ mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0,
+ HDA_INPUT);
+ err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv);
+ mutex_unlock(&codec->spdif_mutex); /* reuse spdif_mutex */
+ return err;
+}
+
+typedef int (*getput_call_t)(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+static int alc_cap_getput_caller(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol,
+ getput_call_t func)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+ unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ int err;
+
+ mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[adc_idx],
+ 3, 0, HDA_INPUT);
+ err = func(kcontrol, ucontrol);
+ mutex_unlock(&codec->spdif_mutex); /* reuse spdif_mutex */
+ return err;
+}
+
+static int alc_cap_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return alc_cap_getput_caller(kcontrol, ucontrol,
+ snd_hda_mixer_amp_volume_get);
+}
+
+static int alc_cap_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return alc_cap_getput_caller(kcontrol, ucontrol,
+ snd_hda_mixer_amp_volume_put);
+}
+
+/* capture mixer elements */
+#define alc_cap_sw_info snd_ctl_boolean_stereo_info
+
+static int alc_cap_sw_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return alc_cap_getput_caller(kcontrol, ucontrol,
+ snd_hda_mixer_amp_switch_get);
+}
+
+static int alc_cap_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return alc_cap_getput_caller(kcontrol, ucontrol,
+ snd_hda_mixer_amp_switch_put);
+}
+
+#define DEFINE_CAPMIX(num) \
+static struct snd_kcontrol_new alc_capture_mixer ## num[] = { \
+ { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = "Capture Switch", \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .count = num, \
+ .info = alc_cap_sw_info, \
+ .get = alc_cap_sw_get, \
+ .put = alc_cap_sw_put, \
+ }, \
+ { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = "Capture Volume", \
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK), \
+ .count = num, \
+ .info = alc_cap_vol_info, \
+ .get = alc_cap_vol_get, \
+ .put = alc_cap_vol_put, \
+ .tlv = { .c = alc_cap_vol_tlv }, \
+ }, \
+ { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ /* .name = "Capture Source", */ \
+ .name = "Input Source", \
+ .count = num, \
+ .info = alc_mux_enum_info, \
+ .get = alc_mux_enum_get, \
+ .put = alc_mux_enum_put, \
+ }, \
+ { } /* end */ \
+}
+
+/* up to three ADCs */
+DEFINE_CAPMIX(1);
+DEFINE_CAPMIX(2);
+DEFINE_CAPMIX(3);
/*
@@ -1533,18 +1657,6 @@ static struct snd_kcontrol_new alc880_tcl_s700_mixer[] = {
HDA_CODEC_MUTE("Mic Playback Switch", 0x0B, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
{ } /* end */
};
@@ -1619,6 +1731,7 @@ static const char *alc_slave_vols[] = {
"Speaker Playback Volume",
"Mono Playback Volume",
"Line-Out Playback Volume",
+ "PCM Playback Volume",
NULL,
};
@@ -1638,6 +1751,9 @@ static const char *alc_slave_sws[] = {
/*
* build control elements
*/
+
+static void alc_free_kctls(struct hda_codec *codec);
+
static int alc_build_controls(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
@@ -1649,7 +1765,11 @@ static int alc_build_controls(struct hda_codec *codec)
if (err < 0)
return err;
}
-
+ if (spec->cap_mixer) {
+ err = snd_hda_add_new_ctls(codec, spec->cap_mixer);
+ if (err < 0)
+ return err;
+ }
if (spec->multiout.dig_out_nid) {
err = snd_hda_create_spdif_out_ctls(codec,
spec->multiout.dig_out_nid);
@@ -1684,6 +1804,7 @@ static int alc_build_controls(struct hda_codec *codec)
return err;
}
+ alc_free_kctls(codec); /* no longer needed */
return 0;
}
@@ -2774,19 +2895,27 @@ static int alc_build_pcms(struct hda_codec *codec)
return 0;
}
+static void alc_free_kctls(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (spec->kctls.list) {
+ struct snd_kcontrol_new *kctl = spec->kctls.list;
+ int i;
+ for (i = 0; i < spec->kctls.used; i++)
+ kfree(kctl[i].name);
+ }
+ snd_array_free(&spec->kctls);
+}
+
static void alc_free(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- unsigned int i;
if (!spec)
return;
- if (spec->kctl_alloc) {
- for (i = 0; i < spec->num_kctl_used; i++)
- kfree(spec->kctl_alloc[i].name);
- kfree(spec->kctl_alloc);
- }
+ alc_free_kctls(codec);
kfree(spec);
codec->spec = NULL; /* to be sure */
}
@@ -3268,6 +3397,8 @@ static struct alc_config_preset alc880_presets[] = {
alc880_gpio2_init_verbs },
.num_dacs = ARRAY_SIZE(alc880_dac_nids),
.dac_nids = alc880_dac_nids,
+ .adc_nids = alc880_adc_nids_alt, /* FIXME: correct? */
+ .num_adc_nids = 1, /* single ADC */
.hp_nid = 0x03,
.num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
.channel_mode = alc880_2_jack_modes,
@@ -3532,9 +3663,6 @@ static struct alc_config_preset alc880_presets[] = {
* Automatic parse of I/O pins from the BIOS configuration
*/
-#define NUM_CONTROL_ALLOC 32
-#define NUM_VERB_ALLOC 32
-
enum {
ALC_CTL_WIDGET_VOL,
ALC_CTL_WIDGET_MUTE,
@@ -3552,29 +3680,15 @@ static int add_control(struct alc_spec *spec, int type, const char *name,
{
struct snd_kcontrol_new *knew;
- if (spec->num_kctl_used >= spec->num_kctl_alloc) {
- int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
-
- /* array + terminator */
- knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL);
- if (!knew)
- return -ENOMEM;
- if (spec->kctl_alloc) {
- memcpy(knew, spec->kctl_alloc,
- sizeof(*knew) * spec->num_kctl_alloc);
- kfree(spec->kctl_alloc);
- }
- spec->kctl_alloc = knew;
- spec->num_kctl_alloc = num;
- }
-
- knew = &spec->kctl_alloc[spec->num_kctl_used];
+ snd_array_init(&spec->kctls, sizeof(*knew), 32);
+ knew = snd_array_new(&spec->kctls);
+ if (!knew)
+ return -ENOMEM;
*knew = alc880_control_templates[type];
knew->name = kstrdup(name, GFP_KERNEL);
if (!knew->name)
return -ENOMEM;
knew->private_value = val;
- spec->num_kctl_used++;
return 0;
}
@@ -3898,10 +4012,10 @@ static int alc880_parse_auto_config(struct hda_codec *codec)
if (spec->autocfg.dig_in_pin)
spec->dig_in_nid = ALC880_DIGIN_NID;
- if (spec->kctl_alloc)
- spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->kctls.list)
+ add_mixer(spec, spec->kctls.list);
- spec->init_verbs[spec->num_init_verbs++] = alc880_volume_init_verbs;
+ add_verb(spec, alc880_volume_init_verbs);
spec->num_mux_defs = 1;
spec->input_mux = &spec->private_imux;
@@ -3925,6 +4039,17 @@ static void alc880_auto_init(struct hda_codec *codec)
* OK, here we have finally the patch for ALC880
*/
+static void set_capture_mixer(struct alc_spec *spec)
+{
+ static struct snd_kcontrol_new *caps[3] = {
+ alc_capture_mixer1,
+ alc_capture_mixer2,
+ alc_capture_mixer3,
+ };
+ if (spec->num_adc_nids > 0 && spec->num_adc_nids < 3)
+ spec->cap_mixer = caps[spec->num_adc_nids - 1];
+}
+
static int patch_alc880(struct hda_codec *codec)
{
struct alc_spec *spec;
@@ -3980,16 +4105,12 @@ static int patch_alc880(struct hda_codec *codec)
if (wcap != AC_WID_AUD_IN) {
spec->adc_nids = alc880_adc_nids_alt;
spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids_alt);
- spec->mixers[spec->num_mixers] =
- alc880_capture_alt_mixer;
- spec->num_mixers++;
} else {
spec->adc_nids = alc880_adc_nids;
spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids);
- spec->mixers[spec->num_mixers] = alc880_capture_mixer;
- spec->num_mixers++;
}
}
+ set_capture_mixer(spec);
spec->vmaster_nid = 0x0c;
@@ -4024,11 +4145,6 @@ static hda_nid_t alc260_adc_nids_alt[1] = {
0x05,
};
-static hda_nid_t alc260_hp_adc_nids[2] = {
- /* ADC1, 0 */
- 0x05, 0x04
-};
-
/* NIDs used when simultaneous access to both ADCs makes sense. Note that
* alc260_capture_mixer assumes ADC0 (nid 0x04) is the first ADC.
*/
@@ -4157,13 +4273,13 @@ static void alc260_hp_master_update(struct hda_codec *codec,
struct alc_spec *spec = codec->spec;
unsigned int val = spec->master_sw ? PIN_HP : 0;
/* change HP and line-out pins */
- snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
val);
- snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ snd_hda_codec_write(codec, line, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
val);
/* mono (speaker) depending on the HP jack sense */
val = (val && !spec->jack_present) ? PIN_OUT : 0;
- snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ snd_hda_codec_write(codec, mono, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
val);
}
@@ -4242,7 +4358,7 @@ static struct snd_kcontrol_new alc260_hp_3013_mixer[] = {
.info = snd_ctl_boolean_mono_info,
.get = alc260_hp_master_sw_get,
.put = alc260_hp_master_sw_put,
- .private_value = (0x10 << 16) | (0x15 << 8) | 0x11
+ .private_value = (0x15 << 16) | (0x10 << 8) | 0x11
},
HDA_CODEC_VOLUME("Front Playback Volume", 0x09, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Front Playback Switch", 0x10, 0x0, HDA_OUTPUT),
@@ -4295,7 +4411,7 @@ static void alc260_hp_3013_automute(struct hda_codec *codec)
present = snd_hda_codec_read(codec, 0x15, 0,
AC_VERB_GET_PIN_SENSE, 0);
spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0;
- alc260_hp_master_update(codec, 0x10, 0x15, 0x11);
+ alc260_hp_master_update(codec, 0x15, 0x10, 0x11);
}
static void alc260_hp_3013_unsol_event(struct hda_codec *codec,
@@ -4427,45 +4543,6 @@ static struct snd_kcontrol_new alc260_replacer_672v_mixer[] = {
{ } /* end */
};
-/* capture mixer elements */
-static struct snd_kcontrol_new alc260_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x04, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x04, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x05, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x05, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc260_capture_alt_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x05, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x05, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
- { } /* end */
-};
-
/*
* initialization verbs
*/
@@ -5282,7 +5359,6 @@ static struct hda_verb alc260_volume_init_verbs[] = {
static int alc260_parse_auto_config(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- unsigned int wcap;
int err;
static hda_nid_t alc260_ignore[] = { 0x17, 0 };
@@ -5293,7 +5369,7 @@ static int alc260_parse_auto_config(struct hda_codec *codec)
err = alc260_auto_create_multi_out_ctls(spec, &spec->autocfg);
if (err < 0)
return err;
- if (!spec->kctl_alloc)
+ if (!spec->kctls.list)
return 0; /* can't find valid BIOS pin config */
err = alc260_auto_create_analog_input_ctls(spec, &spec->autocfg);
if (err < 0)
@@ -5303,28 +5379,14 @@ static int alc260_parse_auto_config(struct hda_codec *codec)
if (spec->autocfg.dig_out_pin)
spec->multiout.dig_out_nid = ALC260_DIGOUT_NID;
- if (spec->kctl_alloc)
- spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->kctls.list)
+ add_mixer(spec, spec->kctls.list);
- spec->init_verbs[spec->num_init_verbs++] = alc260_volume_init_verbs;
+ add_verb(spec, alc260_volume_init_verbs);
spec->num_mux_defs = 1;
spec->input_mux = &spec->private_imux;
- /* check whether NID 0x04 is valid */
- wcap = get_wcaps(codec, 0x04);
- wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; /* get type */
- if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) {
- spec->adc_nids = alc260_adc_nids_alt;
- spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt);
- spec->mixers[spec->num_mixers] = alc260_capture_alt_mixer;
- } else {
- spec->adc_nids = alc260_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids);
- spec->mixers[spec->num_mixers] = alc260_capture_mixer;
- }
- spec->num_mixers++;
-
store_pin_configs(codec);
return 1;
}
@@ -5394,12 +5456,11 @@ static struct alc_config_preset alc260_presets[] = {
[ALC260_BASIC] = {
.mixers = { alc260_base_output_mixer,
alc260_input_mixer,
- alc260_pc_beep_mixer,
- alc260_capture_mixer },
+ alc260_pc_beep_mixer },
.init_verbs = { alc260_init_verbs },
.num_dacs = ARRAY_SIZE(alc260_dac_nids),
.dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_adc_nids),
+ .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids),
.adc_nids = alc260_adc_nids,
.num_channel_mode = ARRAY_SIZE(alc260_modes),
.channel_mode = alc260_modes,
@@ -5407,14 +5468,13 @@ static struct alc_config_preset alc260_presets[] = {
},
[ALC260_HP] = {
.mixers = { alc260_hp_output_mixer,
- alc260_input_mixer,
- alc260_capture_alt_mixer },
+ alc260_input_mixer },
.init_verbs = { alc260_init_verbs,
alc260_hp_unsol_verbs },
.num_dacs = ARRAY_SIZE(alc260_dac_nids),
.dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids),
- .adc_nids = alc260_hp_adc_nids,
+ .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt),
+ .adc_nids = alc260_adc_nids_alt,
.num_channel_mode = ARRAY_SIZE(alc260_modes),
.channel_mode = alc260_modes,
.input_mux = &alc260_capture_source,
@@ -5423,14 +5483,13 @@ static struct alc_config_preset alc260_presets[] = {
},
[ALC260_HP_DC7600] = {
.mixers = { alc260_hp_dc7600_mixer,
- alc260_input_mixer,
- alc260_capture_alt_mixer },
+ alc260_input_mixer },
.init_verbs = { alc260_init_verbs,
alc260_hp_dc7600_verbs },
.num_dacs = ARRAY_SIZE(alc260_dac_nids),
.dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids),
- .adc_nids = alc260_hp_adc_nids,
+ .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt),
+ .adc_nids = alc260_adc_nids_alt,
.num_channel_mode = ARRAY_SIZE(alc260_modes),
.channel_mode = alc260_modes,
.input_mux = &alc260_capture_source,
@@ -5439,14 +5498,13 @@ static struct alc_config_preset alc260_presets[] = {
},
[ALC260_HP_3013] = {
.mixers = { alc260_hp_3013_mixer,
- alc260_input_mixer,
- alc260_capture_alt_mixer },
+ alc260_input_mixer },
.init_verbs = { alc260_hp_3013_init_verbs,
alc260_hp_3013_unsol_verbs },
.num_dacs = ARRAY_SIZE(alc260_dac_nids),
.dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids),
- .adc_nids = alc260_hp_adc_nids,
+ .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt),
+ .adc_nids = alc260_adc_nids_alt,
.num_channel_mode = ARRAY_SIZE(alc260_modes),
.channel_mode = alc260_modes,
.input_mux = &alc260_capture_source,
@@ -5454,8 +5512,7 @@ static struct alc_config_preset alc260_presets[] = {
.init_hook = alc260_hp_3013_automute,
},
[ALC260_FUJITSU_S702X] = {
- .mixers = { alc260_fujitsu_mixer,
- alc260_capture_mixer },
+ .mixers = { alc260_fujitsu_mixer },
.init_verbs = { alc260_fujitsu_init_verbs },
.num_dacs = ARRAY_SIZE(alc260_dac_nids),
.dac_nids = alc260_dac_nids,
@@ -5467,8 +5524,7 @@ static struct alc_config_preset alc260_presets[] = {
.input_mux = alc260_fujitsu_capture_sources,
},
[ALC260_ACER] = {
- .mixers = { alc260_acer_mixer,
- alc260_capture_mixer },
+ .mixers = { alc260_acer_mixer },
.init_verbs = { alc260_acer_init_verbs },
.num_dacs = ARRAY_SIZE(alc260_dac_nids),
.dac_nids = alc260_dac_nids,
@@ -5480,8 +5536,7 @@ static struct alc_config_preset alc260_presets[] = {
.input_mux = alc260_acer_capture_sources,
},
[ALC260_WILL] = {
- .mixers = { alc260_will_mixer,
- alc260_capture_mixer },
+ .mixers = { alc260_will_mixer },
.init_verbs = { alc260_init_verbs, alc260_will_verbs },
.num_dacs = ARRAY_SIZE(alc260_dac_nids),
.dac_nids = alc260_dac_nids,
@@ -5493,8 +5548,7 @@ static struct alc_config_preset alc260_presets[] = {
.input_mux = &alc260_capture_source,
},
[ALC260_REPLACER_672V] = {
- .mixers = { alc260_replacer_672v_mixer,
- alc260_capture_mixer },
+ .mixers = { alc260_replacer_672v_mixer },
.init_verbs = { alc260_init_verbs, alc260_replacer_672v_verbs },
.num_dacs = ARRAY_SIZE(alc260_dac_nids),
.dac_nids = alc260_dac_nids,
@@ -5509,8 +5563,7 @@ static struct alc_config_preset alc260_presets[] = {
},
#ifdef CONFIG_SND_DEBUG
[ALC260_TEST] = {
- .mixers = { alc260_test_mixer,
- alc260_capture_mixer },
+ .mixers = { alc260_test_mixer },
.init_verbs = { alc260_test_init_verbs },
.num_dacs = ARRAY_SIZE(alc260_test_dac_nids),
.dac_nids = alc260_test_dac_nids,
@@ -5569,6 +5622,21 @@ static int patch_alc260(struct hda_codec *codec)
spec->stream_digital_playback = &alc260_pcm_digital_playback;
spec->stream_digital_capture = &alc260_pcm_digital_capture;
+ if (!spec->adc_nids && spec->input_mux) {
+ /* check whether NID 0x04 is valid */
+ unsigned int wcap = get_wcaps(codec, 0x04);
+ wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+ /* get type */
+ if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) {
+ spec->adc_nids = alc260_adc_nids_alt;
+ spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt);
+ } else {
+ spec->adc_nids = alc260_adc_nids;
+ spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids);
+ }
+ }
+ set_capture_mixer(spec);
+
spec->vmaster_nid = 0x08;
codec->patch_ops = alc_patch_ops;
@@ -5625,36 +5693,6 @@ static struct hda_input_mux alc882_capture_source = {
{ "CD", 0x4 },
},
};
-#define alc882_mux_enum_info alc_mux_enum_info
-#define alc882_mux_enum_get alc_mux_enum_get
-
-static int alc882_mux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- const struct hda_input_mux *imux = spec->input_mux;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- hda_nid_t nid = spec->capsrc_nids ?
- spec->capsrc_nids[adc_idx] : spec->adc_nids[adc_idx];
- unsigned int *cur_val = &spec->cur_mux[adc_idx];
- unsigned int i, idx;
-
- idx = ucontrol->value.enumerated.item[0];
- if (idx >= imux->num_items)
- idx = imux->num_items - 1;
- if (*cur_val == idx)
- return 0;
- for (i = 0; i < imux->num_items; i++) {
- unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE;
- snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT,
- imux->items[i].index,
- HDA_AMP_MUTE, v);
- }
- *cur_val = idx;
- return 1;
-}
-
/*
* 2ch mode
*/
@@ -6337,49 +6375,6 @@ static struct hda_verb alc882_auto_init_verbs[] = {
{ }
};
-/* capture mixer elements */
-static struct snd_kcontrol_new alc882_capture_alt_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc882_mux_enum_info,
- .get = alc882_mux_enum_get,
- .put = alc882_mux_enum_put,
- },
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc882_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 3,
- .info = alc882_mux_enum_info,
- .get = alc882_mux_enum_get,
- .put = alc882_mux_enum_put,
- },
- { } /* end */
-};
-
#ifdef CONFIG_SND_HDA_POWER_SAVE
#define alc882_loopbacks alc880_loopbacks
#endif
@@ -6508,8 +6503,7 @@ static struct alc_config_preset alc882_presets[] = {
.init_hook = alc885_imac24_init_hook,
},
[ALC882_TARGA] = {
- .mixers = { alc882_targa_mixer, alc882_chmode_mixer,
- alc882_capture_mixer },
+ .mixers = { alc882_targa_mixer, alc882_chmode_mixer },
.init_verbs = { alc882_init_verbs, alc882_targa_verbs},
.num_dacs = ARRAY_SIZE(alc882_dac_nids),
.dac_nids = alc882_dac_nids,
@@ -6525,8 +6519,7 @@ static struct alc_config_preset alc882_presets[] = {
.init_hook = alc882_targa_automute,
},
[ALC882_ASUS_A7J] = {
- .mixers = { alc882_asus_a7j_mixer, alc882_chmode_mixer,
- alc882_capture_mixer },
+ .mixers = { alc882_asus_a7j_mixer, alc882_chmode_mixer },
.init_verbs = { alc882_init_verbs, alc882_asus_a7j_verbs},
.num_dacs = ARRAY_SIZE(alc882_dac_nids),
.dac_nids = alc882_dac_nids,
@@ -6831,6 +6824,7 @@ static int patch_alc882(struct hda_codec *codec)
spec->stream_digital_playback = &alc882_pcm_digital_playback;
spec->stream_digital_capture = &alc882_pcm_digital_capture;
+ spec->is_mix_capture = 1; /* matrix-style capture */
if (!spec->adc_nids && spec->input_mux) {
/* check whether NID 0x07 is valid */
unsigned int wcap = get_wcaps(codec, 0x07);
@@ -6840,17 +6834,13 @@ static int patch_alc882(struct hda_codec *codec)
spec->adc_nids = alc882_adc_nids_alt;
spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids_alt);
spec->capsrc_nids = alc882_capsrc_nids_alt;
- spec->mixers[spec->num_mixers] =
- alc882_capture_alt_mixer;
- spec->num_mixers++;
} else {
spec->adc_nids = alc882_adc_nids;
spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids);
spec->capsrc_nids = alc882_capsrc_nids;
- spec->mixers[spec->num_mixers] = alc882_capture_mixer;
- spec->num_mixers++;
}
}
+ set_capture_mixer(spec);
spec->vmaster_nid = 0x0c;
@@ -6879,6 +6869,8 @@ static int patch_alc882(struct hda_codec *codec)
#define ALC883_DIGOUT_NID 0x06
#define ALC883_DIGIN_NID 0x0a
+#define ALC1200_DIGOUT_NID 0x10
+
static hda_nid_t alc883_dac_nids[4] = {
/* front, rear, clfe, rear_surr */
0x02, 0x03, 0x04, 0x05
@@ -6889,6 +6881,11 @@ static hda_nid_t alc883_adc_nids[2] = {
0x08, 0x09,
};
+static hda_nid_t alc883_adc_nids_alt[1] = {
+ /* ADC1 */
+ 0x08,
+};
+
static hda_nid_t alc883_capsrc_nids[2] = { 0x23, 0x22 };
/* input MUX */
@@ -6957,11 +6954,6 @@ static struct hda_input_mux alc883_asus_eee1601_capture_source = {
},
};
-#define alc883_mux_enum_info alc_mux_enum_info
-#define alc883_mux_enum_get alc_mux_enum_get
-/* ALC883 has the ALC882-type input selection */
-#define alc883_mux_enum_put alc882_mux_enum_put
-
/*
* 2ch mode
*/
@@ -7115,19 +7107,6 @@ static struct snd_kcontrol_new alc883_base_mixer[] = {
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
{ } /* end */
};
@@ -7145,19 +7124,6 @@ static struct snd_kcontrol_new alc883_mitac_mixer[] = {
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
{ } /* end */
};
@@ -7172,19 +7138,6 @@ static struct snd_kcontrol_new alc883_clevo_m720_mixer[] = {
HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
{ } /* end */
};
@@ -7199,19 +7152,6 @@ static struct snd_kcontrol_new alc883_2ch_fujitsu_pi2515_mixer[] = {
HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
{ } /* end */
};
@@ -7231,19 +7171,6 @@ static struct snd_kcontrol_new alc883_3ST_2ch_mixer[] = {
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
{ } /* end */
};
@@ -7269,17 +7196,6 @@ static struct snd_kcontrol_new alc883_3ST_6ch_mixer[] = {
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
{ } /* end */
};
@@ -7306,19 +7222,6 @@ static struct snd_kcontrol_new alc883_3ST_6ch_intel_mixer[] = {
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
{ } /* end */
};
@@ -7344,18 +7247,6 @@ static struct snd_kcontrol_new alc883_fivestack_mixer[] = {
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
{ } /* end */
};
@@ -7376,19 +7267,6 @@ static struct snd_kcontrol_new alc883_tagra_mixer[] = {
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
{ } /* end */
};
@@ -7404,19 +7282,6 @@ static struct snd_kcontrol_new alc883_tagra_2ch_mixer[] = {
HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
{ } /* end */
};
@@ -7429,17 +7294,6 @@ static struct snd_kcontrol_new alc883_lenovo_101e_2ch_mixer[] = {
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
{ } /* end */
};
@@ -7453,19 +7307,6 @@ static struct snd_kcontrol_new alc883_lenovo_nb0763_mixer[] = {
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("iMic Playback Volume", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_MUTE("iMic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
{ } /* end */
};
@@ -7479,19 +7320,6 @@ static struct snd_kcontrol_new alc883_medion_md2_mixer[] = {
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
{ } /* end */
};
@@ -7504,19 +7332,6 @@ static struct snd_kcontrol_new alc883_acer_aspire_mixer[] = {
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
{ } /* end */
};
@@ -7544,19 +7359,6 @@ static struct snd_kcontrol_new alc888_lenovo_sky_mixer[] = {
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
{ } /* end */
};
@@ -7587,6 +7389,10 @@ static struct snd_kcontrol_new alc883_asus_eee1601_mixer[] = {
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ { } /* end */
+};
+
+static struct snd_kcontrol_new alc883_asus_eee1601_cap_mixer[] = {
HDA_BIND_VOL("Capture Volume", &alc883_bind_cap_vol),
HDA_BIND_SW("Capture Switch", &alc883_bind_cap_switch),
{
@@ -7594,9 +7400,9 @@ static struct snd_kcontrol_new alc883_asus_eee1601_mixer[] = {
/* .name = "Capture Source", */
.name = "Input Source",
.count = 1,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
+ .info = alc_mux_enum_info,
+ .get = alc_mux_enum_get,
+ .put = alc_mux_enum_put,
},
{ } /* end */
};
@@ -8251,27 +8057,6 @@ static struct hda_verb alc883_auto_init_verbs[] = {
{ }
};
-/* capture mixer elements */
-static struct snd_kcontrol_new alc883_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc882_mux_enum_info,
- .get = alc882_mux_enum_get,
- .put = alc882_mux_enum_put,
- },
- { } /* end */
-};
-
static struct hda_verb alc888_asus_m90v_verbs[] = {
{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
@@ -8408,6 +8193,7 @@ static const char *alc883_models[ALC883_MODEL_LAST] = {
[ALC883_CLEVO_M720] = "clevo-m720",
[ALC883_FUJITSU_PI2515] = "fujitsu-pi2515",
[ALC883_3ST_6ch_INTEL] = "3stack-6ch-intel",
+ [ALC1200_ASUS_P5Q] = "asus-p5q",
[ALC883_AUTO] = "auto",
};
@@ -8426,6 +8212,7 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC883_6ST_DIG),
SND_PCI_QUIRK(0x1043, 0x1873, "Asus M90V", ALC888_ASUS_M90V),
SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG),
+ SND_PCI_QUIRK(0x1043, 0x82fe, "Asus P5Q-EM HDMI", ALC1200_ASUS_P5Q),
SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_ASUS_EEE1601),
SND_PCI_QUIRK(0x105b, 0x0ce8, "Foxconn P35AX-S", ALC883_6ST_DIG),
SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC883_6ST_DIG),
@@ -8553,6 +8340,8 @@ static struct alc_config_preset alc883_presets[] = {
.init_verbs = { alc883_init_verbs, alc883_tagra_verbs},
.num_dacs = ARRAY_SIZE(alc883_dac_nids),
.dac_nids = alc883_dac_nids,
+ .adc_nids = alc883_adc_nids_alt,
+ .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt),
.dig_out_nid = ALC883_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
.channel_mode = alc883_3ST_2ch_modes,
@@ -8593,6 +8382,8 @@ static struct alc_config_preset alc883_presets[] = {
alc883_medion_eapd_verbs },
.num_dacs = ARRAY_SIZE(alc883_dac_nids),
.dac_nids = alc883_dac_nids,
+ .adc_nids = alc883_adc_nids_alt,
+ .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt),
.num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
.channel_mode = alc883_sixstack_modes,
.input_mux = &alc883_capture_source,
@@ -8635,6 +8426,8 @@ static struct alc_config_preset alc883_presets[] = {
.init_verbs = { alc883_init_verbs, alc883_lenovo_101e_verbs},
.num_dacs = ARRAY_SIZE(alc883_dac_nids),
.dac_nids = alc883_dac_nids,
+ .adc_nids = alc883_adc_nids_alt,
+ .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt),
.num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
.channel_mode = alc883_3ST_2ch_modes,
.input_mux = &alc883_lenovo_101e_capture_source,
@@ -8731,8 +8524,6 @@ static struct alc_config_preset alc883_presets[] = {
.num_dacs = ARRAY_SIZE(alc883_dac_nids),
.dac_nids = alc883_dac_nids,
.dig_out_nid = ALC883_DIGOUT_NID,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
- .adc_nids = alc883_adc_nids,
.num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
.channel_mode = alc883_sixstack_modes,
.need_dac_fix = 1,
@@ -8756,6 +8547,7 @@ static struct alc_config_preset alc883_presets[] = {
},
[ALC888_ASUS_EEE1601] = {
.mixers = { alc883_asus_eee1601_mixer },
+ .cap_mixer = alc883_asus_eee1601_cap_mixer,
.init_verbs = { alc883_init_verbs, alc888_asus_eee1601_verbs },
.num_dacs = ARRAY_SIZE(alc883_dac_nids),
.dac_nids = alc883_dac_nids,
@@ -8768,6 +8560,17 @@ static struct alc_config_preset alc883_presets[] = {
.unsol_event = alc883_eee1601_unsol_event,
.init_hook = alc883_eee1601_inithook,
},
+ [ALC1200_ASUS_P5Q] = {
+ .mixers = { alc883_base_mixer, alc883_chmode_mixer },
+ .init_verbs = { alc883_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+ .dac_nids = alc883_dac_nids,
+ .dig_out_nid = ALC1200_DIGOUT_NID,
+ .dig_in_nid = ALC883_DIGIN_NID,
+ .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
+ .channel_mode = alc883_sixstack_modes,
+ .input_mux = &alc883_capture_source,
+ },
};
@@ -8862,8 +8665,6 @@ static int alc883_parse_auto_config(struct hda_codec *codec)
/* hack - override the init verbs */
spec->init_verbs[0] = alc883_auto_init_verbs;
- spec->mixers[spec->num_mixers] = alc883_capture_mixer;
- spec->num_mixers++;
return 1; /* config found */
}
@@ -8946,9 +8747,15 @@ static int patch_alc883(struct hda_codec *codec)
spec->stream_digital_playback = &alc883_pcm_digital_playback;
spec->stream_digital_capture = &alc883_pcm_digital_capture;
- spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids);
- spec->adc_nids = alc883_adc_nids;
- spec->capsrc_nids = alc883_capsrc_nids;
+ if (!spec->num_adc_nids) {
+ spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids);
+ spec->adc_nids = alc883_adc_nids;
+ }
+ if (!spec->capsrc_nids)
+ spec->capsrc_nids = alc883_capsrc_nids;
+ spec->is_mix_capture = 1; /* matrix-style capture */
+ if (!spec->cap_mixer)
+ set_capture_mixer(spec);
spec->vmaster_nid = 0x0c;
@@ -9439,20 +9246,6 @@ static struct snd_kcontrol_new alc262_toshiba_s06_mixer[] = {
HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
{ } /* end */
};
@@ -9969,7 +9762,7 @@ static int alc262_ultra_mux_enum_put(struct snd_kcontrol *kcontrol,
struct alc_spec *spec = codec->spec;
int ret;
- ret = alc882_mux_enum_put(kcontrol, ucontrol);
+ ret = alc_mux_enum_put(kcontrol, ucontrol);
if (!ret)
return 0;
/* reprogram the HP pin as mic or HP according to the input source */
@@ -9986,8 +9779,8 @@ static struct snd_kcontrol_new alc262_ultra_capture_mixer[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source",
- .info = alc882_mux_enum_info,
- .get = alc882_mux_enum_get,
+ .info = alc_mux_enum_info,
+ .get = alc_mux_enum_get,
.put = alc262_ultra_mux_enum_put,
},
{ } /* end */
@@ -10380,10 +10173,10 @@ static int alc262_parse_auto_config(struct hda_codec *codec)
if (spec->autocfg.dig_in_pin)
spec->dig_in_nid = ALC262_DIGIN_NID;
- if (spec->kctl_alloc)
- spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->kctls.list)
+ add_mixer(spec, spec->kctls.list);
- spec->init_verbs[spec->num_init_verbs++] = alc262_volume_init_verbs;
+ add_verb(spec, alc262_volume_init_verbs);
spec->num_mux_defs = 1;
spec->input_mux = &spec->private_imux;
@@ -10624,7 +10417,8 @@ static struct alc_config_preset alc262_presets[] = {
.init_hook = alc262_hippo_automute,
},
[ALC262_ULTRA] = {
- .mixers = { alc262_ultra_mixer, alc262_ultra_capture_mixer },
+ .mixers = { alc262_ultra_mixer },
+ .cap_mixer = alc262_ultra_capture_mixer,
.init_verbs = { alc262_ultra_verbs },
.num_dacs = ARRAY_SIZE(alc262_dac_nids),
.dac_nids = alc262_dac_nids,
@@ -10750,6 +10544,7 @@ static int patch_alc262(struct hda_codec *codec)
spec->stream_digital_playback = &alc262_pcm_digital_playback;
spec->stream_digital_capture = &alc262_pcm_digital_capture;
+ spec->is_mix_capture = 1;
if (!spec->adc_nids && spec->input_mux) {
/* check whether NID 0x07 is valid */
unsigned int wcap = get_wcaps(codec, 0x07);
@@ -10760,17 +10555,14 @@ static int patch_alc262(struct hda_codec *codec)
spec->adc_nids = alc262_adc_nids_alt;
spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids_alt);
spec->capsrc_nids = alc262_capsrc_nids_alt;
- spec->mixers[spec->num_mixers] =
- alc262_capture_alt_mixer;
- spec->num_mixers++;
} else {
spec->adc_nids = alc262_adc_nids;
spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids);
spec->capsrc_nids = alc262_capsrc_nids;
- spec->mixers[spec->num_mixers] = alc262_capture_mixer;
- spec->num_mixers++;
}
}
+ if (!spec->cap_mixer)
+ set_capture_mixer(spec);
spec->vmaster_nid = 0x0c;
@@ -10942,6 +10734,22 @@ static struct snd_kcontrol_new alc268_acer_mixer[] = {
{ }
};
+static struct snd_kcontrol_new alc268_acer_dmic_mixer[] = {
+ /* output mixer control */
+ HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = snd_hda_mixer_amp_switch_info,
+ .get = snd_hda_mixer_amp_switch_get,
+ .put = alc268_acer_master_sw_put,
+ .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
+ },
+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line In Boost", 0x1a, 0, HDA_INPUT),
+ { }
+};
+
static struct hda_verb alc268_acer_aspire_one_verbs[] = {
{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
@@ -11218,10 +11026,6 @@ static struct hda_verb alc268_volume_init_verbs[] = {
{ }
};
-#define alc268_mux_enum_info alc_mux_enum_info
-#define alc268_mux_enum_get alc_mux_enum_get
-#define alc268_mux_enum_put alc_mux_enum_put
-
static struct snd_kcontrol_new alc268_capture_alt_mixer[] = {
HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT),
@@ -11233,9 +11037,9 @@ static struct snd_kcontrol_new alc268_capture_alt_mixer[] = {
/* .name = "Capture Source", */
.name = "Input Source",
.count = 1,
- .info = alc268_mux_enum_info,
- .get = alc268_mux_enum_get,
- .put = alc268_mux_enum_put,
+ .info = alc_mux_enum_info,
+ .get = alc_mux_enum_get,
+ .put = alc_mux_enum_put,
},
{ } /* end */
};
@@ -11253,9 +11057,9 @@ static struct snd_kcontrol_new alc268_capture_mixer[] = {
/* .name = "Capture Source", */
.name = "Input Source",
.count = 2,
- .info = alc268_mux_enum_info,
- .get = alc268_mux_enum_get,
- .put = alc268_mux_enum_put,
+ .info = alc_mux_enum_info,
+ .get = alc_mux_enum_get,
+ .put = alc_mux_enum_put,
},
{ } /* end */
};
@@ -11274,6 +11078,15 @@ static struct hda_input_mux alc268_acer_capture_source = {
.num_items = 3,
.items = {
{ "Mic", 0x0 },
+ { "Internal Mic", 0x1 },
+ { "Line", 0x2 },
+ },
+};
+
+static struct hda_input_mux alc268_acer_dmic_capture_source = {
+ .num_items = 3,
+ .items = {
+ { "Mic", 0x0 },
{ "Internal Mic", 0x6 },
{ "Line", 0x2 },
},
@@ -11512,13 +11325,13 @@ static int alc268_parse_auto_config(struct hda_codec *codec)
if (spec->autocfg.dig_out_pin)
spec->multiout.dig_out_nid = ALC268_DIGOUT_NID;
- if (spec->kctl_alloc)
- spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->kctls.list)
+ add_mixer(spec, spec->kctls.list);
if (spec->autocfg.speaker_pins[0] != 0x1d)
- spec->mixers[spec->num_mixers++] = alc268_beep_mixer;
+ add_mixer(spec, alc268_beep_mixer);
- spec->init_verbs[spec->num_init_verbs++] = alc268_volume_init_verbs;
+ add_verb(spec, alc268_volume_init_verbs);
spec->num_mux_defs = 1;
spec->input_mux = &spec->private_imux;
@@ -11554,6 +11367,7 @@ static const char *alc268_models[ALC268_MODEL_LAST] = {
[ALC268_3ST] = "3stack",
[ALC268_TOSHIBA] = "toshiba",
[ALC268_ACER] = "acer",
+ [ALC268_ACER_DMIC] = "acer-dmic",
[ALC268_ACER_ASPIRE_ONE] = "acer-aspire",
[ALC268_DELL] = "dell",
[ALC268_ZEPTO] = "zepto",
@@ -11649,6 +11463,23 @@ static struct alc_config_preset alc268_presets[] = {
.unsol_event = alc268_acer_unsol_event,
.init_hook = alc268_acer_init_hook,
},
+ [ALC268_ACER_DMIC] = {
+ .mixers = { alc268_acer_dmic_mixer, alc268_capture_alt_mixer,
+ alc268_beep_mixer },
+ .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
+ alc268_acer_verbs },
+ .num_dacs = ARRAY_SIZE(alc268_dac_nids),
+ .dac_nids = alc268_dac_nids,
+ .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
+ .adc_nids = alc268_adc_nids_alt,
+ .capsrc_nids = alc268_capsrc_nids,
+ .hp_nid = 0x02,
+ .num_channel_mode = ARRAY_SIZE(alc268_modes),
+ .channel_mode = alc268_modes,
+ .input_mux = &alc268_acer_dmic_capture_source,
+ .unsol_event = alc268_acer_unsol_event,
+ .init_hook = alc268_acer_init_hook,
+ },
[ALC268_ACER_ASPIRE_ONE] = {
.mixers = { alc268_acer_aspire_one_mixer,
alc268_capture_alt_mixer },
@@ -11787,15 +11618,11 @@ static int patch_alc268(struct hda_codec *codec)
if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) {
spec->adc_nids = alc268_adc_nids_alt;
spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt);
- spec->mixers[spec->num_mixers] =
- alc268_capture_alt_mixer;
- spec->num_mixers++;
+ add_mixer(spec, alc268_capture_alt_mixer);
} else {
spec->adc_nids = alc268_adc_nids;
spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids);
- spec->mixers[spec->num_mixers] =
- alc268_capture_mixer;
- spec->num_mixers++;
+ add_mixer(spec, alc268_capture_mixer);
}
spec->capsrc_nids = alc268_capsrc_nids;
/* set default input source */
@@ -11893,6 +11720,31 @@ static struct snd_kcontrol_new alc269_quanta_fl1_mixer[] = {
{ }
};
+static struct snd_kcontrol_new alc269_lifebook_mixer[] = {
+ /* output mixer control */
+ HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = snd_hda_mixer_amp_switch_info,
+ .get = snd_hda_mixer_amp_switch_get,
+ .put = alc268_acer_master_sw_put,
+ .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
+ },
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+ HDA_CODEC_VOLUME("Internal Mic Boost", 0x19, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x0b, 0x03, HDA_INPUT),
+ HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x0b, 0x03, HDA_INPUT),
+ HDA_CODEC_VOLUME("Dock Mic Boost", 0x1b, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x04, HDA_INPUT),
+ { }
+};
+
/* bind volumes of both NID 0x0c and 0x0d */
static struct hda_bind_ctls alc269_epc_bind_vol = {
.ops = &snd_hda_bind_vol,
@@ -11911,28 +11763,18 @@ static struct snd_kcontrol_new alc269_eeepc_mixer[] = {
};
/* capture mixer elements */
-static struct snd_kcontrol_new alc269_capture_mixer[] = {
+static struct snd_kcontrol_new alc269_epc_capture_mixer[] = {
HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
{ } /* end */
};
-/* capture mixer elements */
-static struct snd_kcontrol_new alc269_epc_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+/* FSC amilo */
+static struct snd_kcontrol_new alc269_fujitsu_mixer[] = {
+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+ HDA_BIND_VOL("PCM Playback Volume", &alc269_epc_bind_vol),
{ } /* end */
};
@@ -11953,6 +11795,20 @@ static struct hda_verb alc269_quanta_fl1_verbs[] = {
{ }
};
+static struct hda_verb alc269_lifebook_verbs[] = {
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
+ {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01},
+ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+ {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ { }
+};
+
/* toggle speaker-output according to the hp-jack state */
static void alc269_quanta_fl1_speaker_automute(struct hda_codec *codec)
{
@@ -11978,6 +11834,37 @@ static void alc269_quanta_fl1_speaker_automute(struct hda_codec *codec)
AC_VERB_SET_PROC_COEF, 0x480);
}
+/* toggle speaker-output according to the hp-jacks state */
+static void alc269_lifebook_speaker_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+ unsigned char bits;
+
+ /* Check laptop headphone socket */
+ present = snd_hda_codec_read(codec, 0x15, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+
+ /* Check port replicator headphone socket */
+ present |= snd_hda_codec_read(codec, 0x1a, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+
+ bits = present ? AMP_IN_MUTE(0) : 0;
+ snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
+ AMP_IN_MUTE(0), bits);
+ snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
+ AMP_IN_MUTE(0), bits);
+
+ snd_hda_codec_write(codec, 0x20, 0,
+ AC_VERB_SET_COEF_INDEX, 0x0c);
+ snd_hda_codec_write(codec, 0x20, 0,
+ AC_VERB_SET_PROC_COEF, 0x680);
+
+ snd_hda_codec_write(codec, 0x20, 0,
+ AC_VERB_SET_COEF_INDEX, 0x0c);
+ snd_hda_codec_write(codec, 0x20, 0,
+ AC_VERB_SET_PROC_COEF, 0x480);
+}
+
static void alc269_quanta_fl1_mic_automute(struct hda_codec *codec)
{
unsigned int present;
@@ -11988,6 +11875,29 @@ static void alc269_quanta_fl1_mic_automute(struct hda_codec *codec)
AC_VERB_SET_CONNECT_SEL, present ? 0x0 : 0x1);
}
+static void alc269_lifebook_mic_autoswitch(struct hda_codec *codec)
+{
+ unsigned int present_laptop;
+ unsigned int present_dock;
+
+ present_laptop = snd_hda_codec_read(codec, 0x18, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+
+ present_dock = snd_hda_codec_read(codec, 0x1b, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+
+ /* Laptop mic port overrides dock mic port, design decision */
+ if (present_dock)
+ snd_hda_codec_write(codec, 0x23, 0,
+ AC_VERB_SET_CONNECT_SEL, 0x3);
+ if (present_laptop)
+ snd_hda_codec_write(codec, 0x23, 0,
+ AC_VERB_SET_CONNECT_SEL, 0x0);
+ if (!present_dock && !present_laptop)
+ snd_hda_codec_write(codec, 0x23, 0,
+ AC_VERB_SET_CONNECT_SEL, 0x1);
+}
+
static void alc269_quanta_fl1_unsol_event(struct hda_codec *codec,
unsigned int res)
{
@@ -11997,12 +11907,27 @@ static void alc269_quanta_fl1_unsol_event(struct hda_codec *codec,
alc269_quanta_fl1_mic_automute(codec);
}
+static void alc269_lifebook_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ if ((res >> 26) == ALC880_HP_EVENT)
+ alc269_lifebook_speaker_automute(codec);
+ if ((res >> 26) == ALC880_MIC_EVENT)
+ alc269_lifebook_mic_autoswitch(codec);
+}
+
static void alc269_quanta_fl1_init_hook(struct hda_codec *codec)
{
alc269_quanta_fl1_speaker_automute(codec);
alc269_quanta_fl1_mic_automute(codec);
}
+static void alc269_lifebook_init_hook(struct hda_codec *codec)
+{
+ alc269_lifebook_speaker_automute(codec);
+ alc269_lifebook_mic_autoswitch(codec);
+}
+
static struct hda_verb alc269_eeepc_dmic_init_verbs[] = {
{0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
{0x23, AC_VERB_SET_CONNECT_SEL, 0x05},
@@ -12303,17 +12228,17 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
if (spec->autocfg.dig_out_pin)
spec->multiout.dig_out_nid = ALC269_DIGOUT_NID;
- if (spec->kctl_alloc)
- spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->kctls.list)
+ add_mixer(spec, spec->kctls.list);
/* create a beep mixer control if the pin 0x1d isn't assigned */
for (i = 0; i < ARRAY_SIZE(spec->autocfg.input_pins); i++)
if (spec->autocfg.input_pins[i] == 0x1d)
break;
if (i >= ARRAY_SIZE(spec->autocfg.input_pins))
- spec->mixers[spec->num_mixers++] = alc269_beep_mixer;
+ add_mixer(spec, alc269_beep_mixer);
- spec->init_verbs[spec->num_init_verbs++] = alc269_init_verbs;
+ add_verb(spec, alc269_init_verbs);
spec->num_mux_defs = 1;
spec->input_mux = &spec->private_imux;
/* set default input source */
@@ -12325,8 +12250,8 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
if (err < 0)
return err;
- spec->mixers[spec->num_mixers] = alc269_capture_mixer;
- spec->num_mixers++;
+ if (!spec->cap_mixer)
+ set_capture_mixer(spec);
store_pin_configs(codec);
return 1;
@@ -12355,7 +12280,9 @@ static const char *alc269_models[ALC269_MODEL_LAST] = {
[ALC269_BASIC] = "basic",
[ALC269_QUANTA_FL1] = "quanta",
[ALC269_ASUS_EEEPC_P703] = "eeepc-p703",
- [ALC269_ASUS_EEEPC_P901] = "eeepc-p901"
+ [ALC269_ASUS_EEEPC_P901] = "eeepc-p901",
+ [ALC269_FUJITSU] = "fujitsu",
+ [ALC269_LIFEBOOK] = "lifebook"
};
static struct snd_pci_quirk alc269_cfg_tbl[] = {
@@ -12366,12 +12293,14 @@ static struct snd_pci_quirk alc269_cfg_tbl[] = {
ALC269_ASUS_EEEPC_P901),
SND_PCI_QUIRK(0x1043, 0x834a, "ASUS Eeepc S101",
ALC269_ASUS_EEEPC_P901),
+ SND_PCI_QUIRK(0x1734, 0x115d, "FSC Amilo", ALC269_FUJITSU),
+ SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook ICH9M-based", ALC269_LIFEBOOK),
{}
};
static struct alc_config_preset alc269_presets[] = {
[ALC269_BASIC] = {
- .mixers = { alc269_base_mixer, alc269_capture_mixer },
+ .mixers = { alc269_base_mixer },
.init_verbs = { alc269_init_verbs },
.num_dacs = ARRAY_SIZE(alc269_dac_nids),
.dac_nids = alc269_dac_nids,
@@ -12393,7 +12322,8 @@ static struct alc_config_preset alc269_presets[] = {
.init_hook = alc269_quanta_fl1_init_hook,
},
[ALC269_ASUS_EEEPC_P703] = {
- .mixers = { alc269_eeepc_mixer, alc269_epc_capture_mixer },
+ .mixers = { alc269_eeepc_mixer },
+ .cap_mixer = alc269_epc_capture_mixer,
.init_verbs = { alc269_init_verbs,
alc269_eeepc_amic_init_verbs },
.num_dacs = ARRAY_SIZE(alc269_dac_nids),
@@ -12406,7 +12336,8 @@ static struct alc_config_preset alc269_presets[] = {
.init_hook = alc269_eeepc_amic_inithook,
},
[ALC269_ASUS_EEEPC_P901] = {
- .mixers = { alc269_eeepc_mixer, alc269_epc_capture_mixer},
+ .mixers = { alc269_eeepc_mixer },
+ .cap_mixer = alc269_epc_capture_mixer,
.init_verbs = { alc269_init_verbs,
alc269_eeepc_dmic_init_verbs },
.num_dacs = ARRAY_SIZE(alc269_dac_nids),
@@ -12418,6 +12349,32 @@ static struct alc_config_preset alc269_presets[] = {
.unsol_event = alc269_eeepc_dmic_unsol_event,
.init_hook = alc269_eeepc_dmic_inithook,
},
+ [ALC269_FUJITSU] = {
+ .mixers = { alc269_fujitsu_mixer, alc269_beep_mixer },
+ .cap_mixer = alc269_epc_capture_mixer,
+ .init_verbs = { alc269_init_verbs,
+ alc269_eeepc_dmic_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc269_dac_nids),
+ .dac_nids = alc269_dac_nids,
+ .hp_nid = 0x03,
+ .num_channel_mode = ARRAY_SIZE(alc269_modes),
+ .channel_mode = alc269_modes,
+ .input_mux = &alc269_eeepc_dmic_capture_source,
+ .unsol_event = alc269_eeepc_dmic_unsol_event,
+ .init_hook = alc269_eeepc_dmic_inithook,
+ },
+ [ALC269_LIFEBOOK] = {
+ .mixers = { alc269_lifebook_mixer },
+ .init_verbs = { alc269_init_verbs, alc269_lifebook_verbs },
+ .num_dacs = ARRAY_SIZE(alc269_dac_nids),
+ .dac_nids = alc269_dac_nids,
+ .hp_nid = 0x03,
+ .num_channel_mode = ARRAY_SIZE(alc269_modes),
+ .channel_mode = alc269_modes,
+ .input_mux = &alc269_capture_source,
+ .unsol_event = alc269_lifebook_unsol_event,
+ .init_hook = alc269_lifebook_init_hook,
+ },
};
static int patch_alc269(struct hda_codec *codec)
@@ -12472,6 +12429,8 @@ static int patch_alc269(struct hda_codec *codec)
spec->adc_nids = alc269_adc_nids;
spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids);
spec->capsrc_nids = alc269_capsrc_nids;
+ if (!spec->cap_mixer)
+ set_capture_mixer(spec);
codec->patch_ops = alc_patch_ops;
if (board_config == ALC269_AUTO)
@@ -12612,17 +12571,6 @@ static struct snd_kcontrol_new alc861_base_mixer[] = {
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
- /* Capture mixer control */
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .count = 1,
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
{ } /* end */
};
@@ -12646,17 +12594,6 @@ static struct snd_kcontrol_new alc861_3ST_mixer[] = {
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
- /* Capture mixer control */
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .count = 1,
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Channel Mode",
@@ -12674,18 +12611,6 @@ static struct snd_kcontrol_new alc861_toshiba_mixer[] = {
HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
- /*Capture mixer control */
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .count = 1,
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
-
{ } /* end */
};
@@ -12709,17 +12634,6 @@ static struct snd_kcontrol_new alc861_uniwill_m31_mixer[] = {
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
- /* Capture mixer control */
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .count = 1,
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Channel Mode",
@@ -12751,17 +12665,6 @@ static struct snd_kcontrol_new alc861_asus_mixer[] = {
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_OUTPUT),
- /* Capture mixer control */
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .count = 1,
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Channel Mode",
@@ -13293,25 +13196,6 @@ static int alc861_auto_create_analog_input_ctls(struct alc_spec *spec,
return 0;
}
-static struct snd_kcontrol_new alc861_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
- { } /* end */
-};
-
static void alc861_auto_set_output_and_unmute(struct hda_codec *codec,
hda_nid_t nid,
int pin_type, int dac_idx)
@@ -13402,18 +13286,17 @@ static int alc861_parse_auto_config(struct hda_codec *codec)
if (spec->autocfg.dig_out_pin)
spec->multiout.dig_out_nid = ALC861_DIGOUT_NID;
- if (spec->kctl_alloc)
- spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->kctls.list)
+ add_mixer(spec, spec->kctls.list);
- spec->init_verbs[spec->num_init_verbs++] = alc861_auto_init_verbs;
+ add_verb(spec, alc861_auto_init_verbs);
spec->num_mux_defs = 1;
spec->input_mux = &spec->private_imux;
spec->adc_nids = alc861_adc_nids;
spec->num_adc_nids = ARRAY_SIZE(alc861_adc_nids);
- spec->mixers[spec->num_mixers] = alc861_capture_mixer;
- spec->num_mixers++;
+ set_capture_mixer(spec);
store_pin_configs(codec);
return 1;
@@ -13709,11 +13592,6 @@ static struct hda_input_mux alc861vd_hp_capture_source = {
},
};
-#define alc861vd_mux_enum_info alc_mux_enum_info
-#define alc861vd_mux_enum_get alc_mux_enum_get
-/* ALC861VD has the ALC882-type input selection (but has only one ADC) */
-#define alc861vd_mux_enum_put alc882_mux_enum_put
-
/*
* 2ch mode
*/
@@ -13759,25 +13637,6 @@ static struct snd_kcontrol_new alc861vd_chmode_mixer[] = {
{ } /* end */
};
-static struct snd_kcontrol_new alc861vd_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
-
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = alc861vd_mux_enum_info,
- .get = alc861vd_mux_enum_get,
- .put = alc861vd_mux_enum_put,
- },
- { } /* end */
-};
-
/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17
* Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b
*/
@@ -14169,6 +14028,7 @@ static void alc861vd_dallas_unsol_event(struct hda_codec *codec, unsigned int re
static const char *alc861vd_models[ALC861VD_MODEL_LAST] = {
[ALC660VD_3ST] = "3stack-660",
[ALC660VD_3ST_DIG] = "3stack-660-digout",
+ [ALC660VD_ASUS_V1S] = "asus-v1s",
[ALC861VD_3ST] = "3stack",
[ALC861VD_3ST_DIG] = "3stack-digout",
[ALC861VD_6ST_DIG] = "6stack-digout",
@@ -14183,7 +14043,7 @@ static struct snd_pci_quirk alc861vd_cfg_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_HP),
SND_PCI_QUIRK(0x1043, 0x12e2, "Asus z35m", ALC660VD_3ST),
SND_PCI_QUIRK(0x1043, 0x1339, "Asus G1", ALC660VD_3ST),
- SND_PCI_QUIRK(0x1043, 0x1633, "Asus V1Sn", ALC861VD_LENOVO),
+ SND_PCI_QUIRK(0x1043, 0x1633, "Asus V1Sn", ALC660VD_ASUS_V1S),
SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660VD_3ST_DIG),
SND_PCI_QUIRK(0x10de, 0x03f0, "Realtek ALC660 demo", ALC660VD_3ST),
SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO),
@@ -14290,6 +14150,21 @@ static struct alc_config_preset alc861vd_presets[] = {
.unsol_event = alc861vd_dallas_unsol_event,
.init_hook = alc861vd_dallas_automute,
},
+ [ALC660VD_ASUS_V1S] = {
+ .mixers = { alc861vd_lenovo_mixer },
+ .init_verbs = { alc861vd_volume_init_verbs,
+ alc861vd_3stack_init_verbs,
+ alc861vd_eapd_verbs,
+ alc861vd_lenovo_unsol_verbs },
+ .num_dacs = ARRAY_SIZE(alc660vd_dac_nids),
+ .dac_nids = alc660vd_dac_nids,
+ .dig_out_nid = ALC861VD_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
+ .channel_mode = alc861vd_3stack_2ch_modes,
+ .input_mux = &alc861vd_capture_source,
+ .unsol_event = alc861vd_lenovo_unsol_event,
+ .init_hook = alc861vd_lenovo_automute,
+ },
};
/*
@@ -14514,11 +14389,10 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec)
if (spec->autocfg.dig_out_pin)
spec->multiout.dig_out_nid = ALC861VD_DIGOUT_NID;
- if (spec->kctl_alloc)
- spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->kctls.list)
+ add_mixer(spec, spec->kctls.list);
- spec->init_verbs[spec->num_init_verbs++]
- = alc861vd_volume_init_verbs;
+ add_verb(spec, alc861vd_volume_init_verbs);
spec->num_mux_defs = 1;
spec->input_mux = &spec->private_imux;
@@ -14585,7 +14459,7 @@ static int patch_alc861vd(struct hda_codec *codec)
spec->stream_name_analog = "ALC660-VD Analog";
spec->stream_name_digital = "ALC660-VD Digital";
/* always turn on EAPD */
- spec->init_verbs[spec->num_init_verbs++] = alc660vd_eapd_verbs;
+ add_verb(spec, alc660vd_eapd_verbs);
} else {
spec->stream_name_analog = "ALC861VD Analog";
spec->stream_name_digital = "ALC861VD Digital";
@@ -14600,9 +14474,9 @@ static int patch_alc861vd(struct hda_codec *codec)
spec->adc_nids = alc861vd_adc_nids;
spec->num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids);
spec->capsrc_nids = alc861vd_capsrc_nids;
+ spec->is_mix_capture = 1;
- spec->mixers[spec->num_mixers] = alc861vd_capture_mixer;
- spec->num_mixers++;
+ set_capture_mixer(spec);
spec->vmaster_nid = 0x02;
@@ -14689,10 +14563,6 @@ static struct hda_input_mux alc663_m51va_capture_source = {
},
};
-#define alc662_mux_enum_info alc_mux_enum_info
-#define alc662_mux_enum_get alc_mux_enum_get
-#define alc662_mux_enum_put alc882_mux_enum_put
-
/*
* 2ch mode
*/
@@ -15278,25 +15148,6 @@ static struct hda_verb alc662_ecs_init_verbs[] = {
{}
};
-/* capture mixer elements */
-static struct snd_kcontrol_new alc662_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = alc662_mux_enum_info,
- .get = alc662_mux_enum_get,
- .put = alc662_mux_enum_put,
- },
- { } /* end */
-};
-
static struct snd_kcontrol_new alc662_auto_capture_mixer[] = {
HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
@@ -15868,7 +15719,7 @@ static struct snd_pci_quirk alc662_cfg_tbl[] = {
static struct alc_config_preset alc662_presets[] = {
[ALC662_3ST_2ch_DIG] = {
- .mixers = { alc662_3ST_2ch_mixer, alc662_capture_mixer },
+ .mixers = { alc662_3ST_2ch_mixer },
.init_verbs = { alc662_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
.dac_nids = alc662_dac_nids,
@@ -15879,8 +15730,7 @@ static struct alc_config_preset alc662_presets[] = {
.input_mux = &alc662_capture_source,
},
[ALC662_3ST_6ch_DIG] = {
- .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer,
- alc662_capture_mixer },
+ .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer },
.init_verbs = { alc662_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
.dac_nids = alc662_dac_nids,
@@ -15892,8 +15742,7 @@ static struct alc_config_preset alc662_presets[] = {
.input_mux = &alc662_capture_source,
},
[ALC662_3ST_6ch] = {
- .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer,
- alc662_capture_mixer },
+ .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer },
.init_verbs = { alc662_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
.dac_nids = alc662_dac_nids,
@@ -15903,8 +15752,7 @@ static struct alc_config_preset alc662_presets[] = {
.input_mux = &alc662_capture_source,
},
[ALC662_5ST_DIG] = {
- .mixers = { alc662_base_mixer, alc662_chmode_mixer,
- alc662_capture_mixer },
+ .mixers = { alc662_base_mixer, alc662_chmode_mixer },
.init_verbs = { alc662_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
.dac_nids = alc662_dac_nids,
@@ -15915,7 +15763,7 @@ static struct alc_config_preset alc662_presets[] = {
.input_mux = &alc662_capture_source,
},
[ALC662_LENOVO_101E] = {
- .mixers = { alc662_lenovo_101e_mixer, alc662_capture_mixer },
+ .mixers = { alc662_lenovo_101e_mixer },
.init_verbs = { alc662_init_verbs, alc662_sue_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
.dac_nids = alc662_dac_nids,
@@ -15926,7 +15774,7 @@ static struct alc_config_preset alc662_presets[] = {
.init_hook = alc662_lenovo_101e_all_automute,
},
[ALC662_ASUS_EEEPC_P701] = {
- .mixers = { alc662_eeepc_p701_mixer, alc662_capture_mixer },
+ .mixers = { alc662_eeepc_p701_mixer },
.init_verbs = { alc662_init_verbs,
alc662_eeepc_sue_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
@@ -15938,7 +15786,7 @@ static struct alc_config_preset alc662_presets[] = {
.init_hook = alc662_eeepc_inithook,
},
[ALC662_ASUS_EEEPC_EP20] = {
- .mixers = { alc662_eeepc_ep20_mixer, alc662_capture_mixer,
+ .mixers = { alc662_eeepc_ep20_mixer,
alc662_chmode_mixer },
.init_verbs = { alc662_init_verbs,
alc662_eeepc_ep20_sue_init_verbs },
@@ -15951,7 +15799,7 @@ static struct alc_config_preset alc662_presets[] = {
.init_hook = alc662_eeepc_ep20_inithook,
},
[ALC662_ECS] = {
- .mixers = { alc662_ecs_mixer, alc662_capture_mixer },
+ .mixers = { alc662_ecs_mixer },
.init_verbs = { alc662_init_verbs,
alc662_ecs_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
@@ -15963,7 +15811,7 @@ static struct alc_config_preset alc662_presets[] = {
.init_hook = alc662_eeepc_inithook,
},
[ALC663_ASUS_M51VA] = {
- .mixers = { alc663_m51va_mixer, alc662_capture_mixer},
+ .mixers = { alc663_m51va_mixer },
.init_verbs = { alc662_init_verbs, alc663_m51va_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
.dac_nids = alc662_dac_nids,
@@ -15975,7 +15823,7 @@ static struct alc_config_preset alc662_presets[] = {
.init_hook = alc663_m51va_inithook,
},
[ALC663_ASUS_G71V] = {
- .mixers = { alc663_g71v_mixer, alc662_capture_mixer},
+ .mixers = { alc663_g71v_mixer },
.init_verbs = { alc662_init_verbs, alc663_g71v_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
.dac_nids = alc662_dac_nids,
@@ -15987,7 +15835,7 @@ static struct alc_config_preset alc662_presets[] = {
.init_hook = alc663_g71v_inithook,
},
[ALC663_ASUS_H13] = {
- .mixers = { alc663_m51va_mixer, alc662_capture_mixer},
+ .mixers = { alc663_m51va_mixer },
.init_verbs = { alc662_init_verbs, alc663_m51va_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
.dac_nids = alc662_dac_nids,
@@ -15998,7 +15846,7 @@ static struct alc_config_preset alc662_presets[] = {
.init_hook = alc663_m51va_inithook,
},
[ALC663_ASUS_G50V] = {
- .mixers = { alc663_g50v_mixer, alc662_capture_mixer},
+ .mixers = { alc663_g50v_mixer },
.init_verbs = { alc662_init_verbs, alc663_g50v_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
.dac_nids = alc662_dac_nids,
@@ -16010,7 +15858,8 @@ static struct alc_config_preset alc662_presets[] = {
.init_hook = alc663_g50v_inithook,
},
[ALC663_ASUS_MODE1] = {
- .mixers = { alc663_m51va_mixer, alc662_auto_capture_mixer },
+ .mixers = { alc663_m51va_mixer },
+ .cap_mixer = alc662_auto_capture_mixer,
.init_verbs = { alc662_init_verbs,
alc663_21jd_amic_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
@@ -16024,7 +15873,8 @@ static struct alc_config_preset alc662_presets[] = {
.init_hook = alc663_mode1_inithook,
},
[ALC662_ASUS_MODE2] = {
- .mixers = { alc662_1bjd_mixer, alc662_auto_capture_mixer },
+ .mixers = { alc662_1bjd_mixer },
+ .cap_mixer = alc662_auto_capture_mixer,
.init_verbs = { alc662_init_verbs,
alc662_1bjd_amic_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
@@ -16037,7 +15887,8 @@ static struct alc_config_preset alc662_presets[] = {
.init_hook = alc662_mode2_inithook,
},
[ALC663_ASUS_MODE3] = {
- .mixers = { alc663_two_hp_m1_mixer, alc662_auto_capture_mixer },
+ .mixers = { alc663_two_hp_m1_mixer },
+ .cap_mixer = alc662_auto_capture_mixer,
.init_verbs = { alc662_init_verbs,
alc663_two_hp_amic_m1_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
@@ -16051,8 +15902,8 @@ static struct alc_config_preset alc662_presets[] = {
.init_hook = alc663_mode3_inithook,
},
[ALC663_ASUS_MODE4] = {
- .mixers = { alc663_asus_21jd_clfe_mixer,
- alc662_auto_capture_mixer},
+ .mixers = { alc663_asus_21jd_clfe_mixer },
+ .cap_mixer = alc662_auto_capture_mixer,
.init_verbs = { alc662_init_verbs,
alc663_21jd_amic_init_verbs},
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
@@ -16066,8 +15917,8 @@ static struct alc_config_preset alc662_presets[] = {
.init_hook = alc663_mode4_inithook,
},
[ALC663_ASUS_MODE5] = {
- .mixers = { alc663_asus_15jd_clfe_mixer,
- alc662_auto_capture_mixer },
+ .mixers = { alc663_asus_15jd_clfe_mixer },
+ .cap_mixer = alc662_auto_capture_mixer,
.init_verbs = { alc662_init_verbs,
alc663_15jd_amic_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
@@ -16081,7 +15932,8 @@ static struct alc_config_preset alc662_presets[] = {
.init_hook = alc663_mode5_inithook,
},
[ALC663_ASUS_MODE6] = {
- .mixers = { alc663_two_hp_m2_mixer, alc662_auto_capture_mixer },
+ .mixers = { alc663_two_hp_m2_mixer },
+ .cap_mixer = alc662_auto_capture_mixer,
.init_verbs = { alc662_init_verbs,
alc663_two_hp_amic_m2_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
@@ -16342,24 +16194,20 @@ static int alc662_parse_auto_config(struct hda_codec *codec)
if (spec->autocfg.dig_out_pin)
spec->multiout.dig_out_nid = ALC880_DIGOUT_NID;
- if (spec->kctl_alloc)
- spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->kctls.list)
+ add_mixer(spec, spec->kctls.list);
spec->num_mux_defs = 1;
spec->input_mux = &spec->private_imux;
- spec->init_verbs[spec->num_init_verbs++] = alc662_auto_init_verbs;
+ add_verb(spec, alc662_auto_init_verbs);
if (codec->vendor_id == 0x10ec0663)
- spec->init_verbs[spec->num_init_verbs++] =
- alc663_auto_init_verbs;
+ add_verb(spec, alc663_auto_init_verbs);
err = alc_auto_add_mic_boost(codec);
if (err < 0)
return err;
- spec->mixers[spec->num_mixers] = alc662_capture_mixer;
- spec->num_mixers++;
-
store_pin_configs(codec);
return 1;
}
@@ -16435,6 +16283,10 @@ static int patch_alc662(struct hda_codec *codec)
spec->adc_nids = alc662_adc_nids;
spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids);
spec->capsrc_nids = alc662_capsrc_nids;
+ spec->is_mix_capture = 1;
+
+ if (!spec->cap_mixer)
+ set_capture_mixer(spec);
spec->vmaster_nid = 0x02;
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 9563b5bbb272..139efe37f3b3 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -30,13 +30,12 @@
#include <linux/pci.h>
#include <sound/core.h>
#include <sound/asoundef.h>
+#include <sound/jack.h>
#include "hda_codec.h"
#include "hda_local.h"
#include "hda_patch.h"
#include "hda_beep.h"
-#define NUM_CONTROL_ALLOC 32
-
#define STAC_VREF_EVENT 0x00
#define STAC_INSERT_EVENT 0x10
#define STAC_PWR_EVENT 0x20
@@ -132,11 +131,23 @@ enum {
STAC_927X_MODELS
};
+struct sigmatel_event {
+ hda_nid_t nid;
+ int data;
+};
+
+struct sigmatel_jack {
+ hda_nid_t nid;
+ int type;
+ struct snd_jack *jack;
+};
+
struct sigmatel_spec {
struct snd_kcontrol_new *mixers[4];
unsigned int num_mixers;
int board_config;
+ unsigned int eapd_switch: 1;
unsigned int surr_switch: 1;
unsigned int line_switch: 1;
unsigned int mic_switch: 1;
@@ -164,6 +175,12 @@ struct sigmatel_spec {
hda_nid_t *pwr_nids;
hda_nid_t *dac_list;
+ /* jack detection */
+ struct snd_array jacks;
+
+ /* events */
+ struct snd_array events;
+
/* playback */
struct hda_input_mux *mono_mux;
struct hda_input_mux *amp_mux;
@@ -193,7 +210,6 @@ struct sigmatel_spec {
hda_nid_t *pin_nids;
unsigned int num_pins;
unsigned int *pin_configs;
- unsigned int *bios_pin_configs;
/* codec specific stuff */
struct hda_verb *init;
@@ -221,8 +237,7 @@ struct sigmatel_spec {
/* dynamic controls and input_mux */
struct auto_pin_cfg autocfg;
- unsigned int num_kctl_alloc, num_kctl_used;
- struct snd_kcontrol_new *kctl_alloc;
+ struct snd_array kctls;
struct hda_input_mux private_dimux;
struct hda_input_mux private_imux;
struct hda_input_mux private_smux;
@@ -1232,9 +1247,14 @@ static const char *slave_sws[] = {
NULL
};
+static void stac92xx_free_kctls(struct hda_codec *codec);
+static int stac92xx_add_jack(struct hda_codec *codec, hda_nid_t nid, int type);
+
static int stac92xx_build_controls(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t nid;
int err;
int i;
@@ -1249,7 +1269,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
}
if (spec->num_dmuxes > 0) {
stac_dmux_mixer.count = spec->num_dmuxes;
- err = snd_ctl_add(codec->bus->card,
+ err = snd_hda_ctl_add(codec,
snd_ctl_new1(&stac_dmux_mixer, codec));
if (err < 0)
return err;
@@ -1304,6 +1324,37 @@ static int stac92xx_build_controls(struct hda_codec *codec)
return err;
}
+ stac92xx_free_kctls(codec); /* no longer needed */
+
+ /* create jack input elements */
+ if (spec->hp_detect) {
+ for (i = 0; i < cfg->hp_outs; i++) {
+ int type = SND_JACK_HEADPHONE;
+ nid = cfg->hp_pins[i];
+ /* jack detection */
+ if (cfg->hp_outs == i)
+ type |= SND_JACK_LINEOUT;
+ err = stac92xx_add_jack(codec, nid, type);
+ if (err < 0)
+ return err;
+ }
+ }
+ for (i = 0; i < cfg->line_outs; i++) {
+ err = stac92xx_add_jack(codec, cfg->line_out_pins[i],
+ SND_JACK_LINEOUT);
+ if (err < 0)
+ return err;
+ }
+ for (i = 0; i < AUTO_PIN_LAST; i++) {
+ nid = cfg->input_pins[i];
+ if (nid) {
+ err = stac92xx_add_jack(codec, nid,
+ SND_JACK_MICROPHONE);
+ if (err < 0)
+ return err;
+ }
+ }
+
return 0;
}
@@ -2171,12 +2222,11 @@ static int stac92xx_save_bios_config_regs(struct hda_codec *codec)
int i;
struct sigmatel_spec *spec = codec->spec;
- if (! spec->bios_pin_configs) {
- spec->bios_pin_configs = kcalloc(spec->num_pins,
- sizeof(*spec->bios_pin_configs), GFP_KERNEL);
- if (! spec->bios_pin_configs)
- return -ENOMEM;
- }
+ kfree(spec->pin_configs);
+ spec->pin_configs = kcalloc(spec->num_pins, sizeof(*spec->pin_configs),
+ GFP_KERNEL);
+ if (!spec->pin_configs)
+ return -ENOMEM;
for (i = 0; i < spec->num_pins; i++) {
hda_nid_t nid = spec->pin_nids[i];
@@ -2186,7 +2236,7 @@ static int stac92xx_save_bios_config_regs(struct hda_codec *codec)
AC_VERB_GET_CONFIG_DEFAULT, 0x00);
snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x bios pin config %8.8x\n",
nid, pin_cfg);
- spec->bios_pin_configs[i] = pin_cfg;
+ spec->pin_configs[i] = pin_cfg;
}
return 0;
@@ -2228,6 +2278,39 @@ static void stac92xx_set_config_regs(struct hda_codec *codec)
spec->pin_configs[i]);
}
+static int stac_save_pin_cfgs(struct hda_codec *codec, unsigned int *pins)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (!pins)
+ return stac92xx_save_bios_config_regs(codec);
+
+ kfree(spec->pin_configs);
+ spec->pin_configs = kmemdup(pins,
+ spec->num_pins * sizeof(*pins),
+ GFP_KERNEL);
+ if (!spec->pin_configs)
+ return -ENOMEM;
+
+ stac92xx_set_config_regs(codec);
+ return 0;
+}
+
+static void stac_change_pin_config(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int cfg)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < spec->num_pins; i++) {
+ if (spec->pin_nids[i] == nid) {
+ spec->pin_configs[i] = cfg;
+ stac92xx_set_config_reg(codec, nid, cfg);
+ break;
+ }
+ }
+}
+
/*
* Analog playback callbacks
*/
@@ -2465,7 +2548,7 @@ static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol,
/* check to be sure that the ports are upto date with
* switch changes
*/
- codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
+ codec->patch_ops.unsol_event(codec, (STAC_HP_EVENT | nid) << 26);
return 1;
}
@@ -2505,7 +2588,8 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
* appropriately according to the pin direction
*/
if (spec->hp_detect)
- codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
+ codec->patch_ops.unsol_event(codec,
+ (STAC_HP_EVENT | nid) << 26);
return 1;
}
@@ -2600,28 +2684,16 @@ static int stac92xx_add_control_temp(struct sigmatel_spec *spec,
{
struct snd_kcontrol_new *knew;
- if (spec->num_kctl_used >= spec->num_kctl_alloc) {
- int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
-
- knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */
- if (! knew)
- return -ENOMEM;
- if (spec->kctl_alloc) {
- memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc);
- kfree(spec->kctl_alloc);
- }
- spec->kctl_alloc = knew;
- spec->num_kctl_alloc = num;
- }
-
- knew = &spec->kctl_alloc[spec->num_kctl_used];
+ snd_array_init(&spec->kctls, sizeof(*knew), 32);
+ knew = snd_array_new(&spec->kctls);
+ if (!knew)
+ return -ENOMEM;
*knew = *ktemp;
knew->index = idx;
knew->name = kstrdup(name, GFP_KERNEL);
if (!knew->name)
return -ENOMEM;
knew->private_value = val;
- spec->num_kctl_used++;
return 0;
}
@@ -3498,8 +3570,8 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
if (dig_in && spec->autocfg.dig_in_pin)
spec->dig_in_nid = dig_in;
- if (spec->kctl_alloc)
- spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->kctls.list)
+ spec->mixers[spec->num_mixers++] = spec->kctls.list;
spec->input_mux = &spec->private_imux;
spec->dinput_mux = &spec->private_dimux;
@@ -3606,8 +3678,8 @@ static int stac9200_parse_auto_config(struct hda_codec *codec)
if (spec->autocfg.dig_in_pin)
spec->dig_in_nid = 0x04;
- if (spec->kctl_alloc)
- spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->kctls.list)
+ spec->mixers[spec->num_mixers++] = spec->kctls.list;
spec->input_mux = &spec->private_imux;
spec->dinput_mux = &spec->private_dimux;
@@ -3651,13 +3723,74 @@ static void stac_gpio_set(struct hda_codec *codec, unsigned int mask,
AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */
}
+static int stac92xx_add_jack(struct hda_codec *codec,
+ hda_nid_t nid, int type)
+{
+#ifdef CONFIG_SND_JACK
+ struct sigmatel_spec *spec = codec->spec;
+ struct sigmatel_jack *jack;
+ int def_conf = snd_hda_codec_read(codec, nid,
+ 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+ int connectivity = get_defcfg_connect(def_conf);
+ char name[32];
+
+ if (connectivity && connectivity != AC_JACK_PORT_FIXED)
+ return 0;
+
+ snd_array_init(&spec->jacks, sizeof(*jack), 32);
+ jack = snd_array_new(&spec->jacks);
+ if (!jack)
+ return -ENOMEM;
+ jack->nid = nid;
+ jack->type = type;
+
+ sprintf(name, "%s at %s %s Jack",
+ snd_hda_get_jack_type(def_conf),
+ snd_hda_get_jack_connectivity(def_conf),
+ snd_hda_get_jack_location(def_conf));
+
+ return snd_jack_new(codec->bus->card, name, type, &jack->jack);
+#else
+ return 0;
+#endif
+}
+
+static int stac92xx_add_event(struct sigmatel_spec *spec, hda_nid_t nid,
+ int data)
+{
+ struct sigmatel_event *event;
+
+ snd_array_init(&spec->events, sizeof(*event), 32);
+ event = snd_array_new(&spec->events);
+ if (!event)
+ return -ENOMEM;
+ event->nid = nid;
+ event->data = data;
+
+ return 0;
+}
+
+static int stac92xx_event_data(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct sigmatel_event *events = spec->events.list;
+ if (events) {
+ int i;
+ for (i = 0; i < spec->events.used; i++)
+ if (events[i].nid == nid)
+ return events[i].data;
+ }
+ return 0;
+}
+
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)
+ 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));
+ (AC_USRSP_EN | event | nid));
+ }
}
static int is_nid_hp_pin(struct auto_pin_cfg *cfg, hda_nid_t nid)
@@ -3700,17 +3833,18 @@ static int stac92xx_init(struct hda_codec *codec)
/* set up pins */
if (spec->hp_detect) {
/* Enable unsolicited responses on the HP widget */
- for (i = 0; i < cfg->hp_outs; i++)
- enable_pin_detect(codec, cfg->hp_pins[i],
- STAC_HP_EVENT);
+ for (i = 0; i < cfg->hp_outs; i++) {
+ hda_nid_t nid = cfg->hp_pins[i];
+ enable_pin_detect(codec, nid, STAC_HP_EVENT | nid);
+ }
/* force to enable the first line-out; the others are set up
* in unsol_event
*/
stac92xx_auto_set_pinctl(codec, spec->autocfg.line_out_pins[0],
- AC_PINCTL_OUT_EN);
- stac92xx_auto_init_hp_out(codec);
+ AC_PINCTL_OUT_EN);
/* fake event to set up pins */
- codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
+ codec->patch_ops.unsol_event(codec,
+ (STAC_HP_EVENT | spec->autocfg.hp_pins[0]) << 26);
} else {
stac92xx_auto_init_multi_out(codec);
stac92xx_auto_init_hp_out(codec);
@@ -3731,6 +3865,7 @@ static int stac92xx_init(struct hda_codec *codec)
}
pinctl |= AC_PINCTL_IN_EN;
stac92xx_auto_set_pinctl(codec, nid, pinctl);
+ enable_pin_detect(codec, nid, STAC_INSERT_EVENT | nid);
}
}
for (i = 0; i < spec->num_dmics; i++)
@@ -3772,22 +3907,43 @@ static int stac92xx_init(struct hda_codec *codec)
return 0;
}
+static void stac92xx_free_jacks(struct hda_codec *codec)
+{
+#ifdef CONFIG_SND_JACK
+ struct sigmatel_spec *spec = codec->spec;
+ if (spec->jacks.list) {
+ struct sigmatel_jack *jacks = spec->jacks.list;
+ int i;
+ for (i = 0; i < spec->jacks.used; i++)
+ snd_device_free(codec->bus->card, &jacks[i].jack);
+ }
+ snd_array_free(&spec->jacks);
+#endif
+}
+
+static void stac92xx_free_kctls(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (spec->kctls.list) {
+ struct snd_kcontrol_new *kctl = spec->kctls.list;
+ int i;
+ for (i = 0; i < spec->kctls.used; i++)
+ kfree(kctl[i].name);
+ }
+ snd_array_free(&spec->kctls);
+}
+
static void stac92xx_free(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
- int i;
if (! spec)
return;
- if (spec->kctl_alloc) {
- for (i = 0; i < spec->num_kctl_used; i++)
- kfree(spec->kctl_alloc[i].name);
- kfree(spec->kctl_alloc);
- }
-
- if (spec->bios_pin_configs)
- kfree(spec->bios_pin_configs);
+ kfree(spec->pin_configs);
+ stac92xx_free_jacks(codec);
+ snd_array_free(&spec->events);
kfree(spec);
snd_hda_detach_beep_device(codec);
@@ -3901,7 +4057,7 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
for (i = 0; i < cfg->speaker_outs; i++)
stac92xx_reset_pinctl(codec, cfg->speaker_pins[i],
AC_PINCTL_OUT_EN);
- if (spec->eapd_mask)
+ if (spec->eapd_mask && spec->eapd_switch)
stac_gpio_set(codec, spec->gpio_mask,
spec->gpio_dir, spec->gpio_data &
~spec->eapd_mask);
@@ -3916,7 +4072,7 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
for (i = 0; i < cfg->speaker_outs; i++)
stac92xx_set_pinctl(codec, cfg->speaker_pins[i],
AC_PINCTL_OUT_EN);
- if (spec->eapd_mask)
+ if (spec->eapd_mask && spec->eapd_switch)
stac_gpio_set(codec, spec->gpio_mask,
spec->gpio_dir, spec->gpio_data |
spec->eapd_mask);
@@ -3955,24 +4111,57 @@ static void stac92xx_pin_sense(struct hda_codec *codec, int idx)
/* power down unused output ports */
snd_hda_codec_write(codec, codec->afg, 0, 0x7ec, val);
-};
+}
+
+static void stac92xx_report_jack(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct sigmatel_jack *jacks = spec->jacks.list;
+
+ if (jacks) {
+ int i;
+ for (i = 0; i < spec->jacks.used; i++) {
+ if (jacks->nid == nid) {
+ unsigned int pin_ctl =
+ snd_hda_codec_read(codec, nid,
+ 0, AC_VERB_GET_PIN_WIDGET_CONTROL,
+ 0x00);
+ int type = jacks->type;
+ if (type == (SND_JACK_LINEOUT
+ | SND_JACK_HEADPHONE))
+ type = (pin_ctl & AC_PINCTL_HP_EN)
+ ? SND_JACK_HEADPHONE : SND_JACK_LINEOUT;
+ snd_jack_report(jacks->jack,
+ get_hp_pin_presence(codec, nid)
+ ? type : 0);
+ }
+ jacks++;
+ }
+ }
+}
static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
{
struct sigmatel_spec *spec = codec->spec;
- int idx = res >> 26 & 0x0f;
+ int event = (res >> 26) & 0x70;
+ int nid = res >> 26 & 0x0f;
- switch ((res >> 26) & 0x70) {
+ switch (event) {
case STAC_HP_EVENT:
stac92xx_hp_detect(codec, res);
/* fallthru */
+ case STAC_INSERT_EVENT:
case STAC_PWR_EVENT:
- if (spec->num_pwrs > 0)
- stac92xx_pin_sense(codec, idx);
+ if (nid) {
+ if (spec->num_pwrs > 0)
+ stac92xx_pin_sense(codec, nid);
+ stac92xx_report_jack(codec, nid);
+ }
break;
case STAC_VREF_EVENT: {
int data = snd_hda_codec_read(codec, codec->afg, 0,
AC_VERB_GET_GPIO_DATA, 0);
+ int idx = stac92xx_event_data(codec, nid);
/* toggle VREF state based on GPIOx status */
snd_hda_codec_write(codec, codec->afg, 0, 0x7e0,
!!(data & (1 << idx)));
@@ -3987,17 +4176,13 @@ static int stac92xx_resume(struct hda_codec *codec)
struct sigmatel_spec *spec = codec->spec;
stac92xx_set_config_regs(codec);
- snd_hda_sequence_write(codec, spec->init);
- stac_gpio_set(codec, spec->gpio_mask,
- spec->gpio_dir, spec->gpio_data);
+ stac92xx_init(codec);
snd_hda_codec_resume_amp(codec);
snd_hda_codec_resume_cache(codec);
- /* power down inactive DACs */
- if (spec->dac_list)
- stac92xx_power_down(codec);
- /* invoke unsolicited event to reset the HP state */
+ /* fake event to set up pins again to override cached values */
if (spec->hp_detect)
- codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
+ codec->patch_ops.unsol_event(codec,
+ (STAC_HP_EVENT | spec->autocfg.hp_pins[0]) << 26);
return 0;
}
#endif
@@ -4031,14 +4216,12 @@ static int patch_stac9200(struct hda_codec *codec)
if (spec->board_config < 0) {
snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9200, using BIOS defaults\n");
err = stac92xx_save_bios_config_regs(codec);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
- spec->pin_configs = spec->bios_pin_configs;
- } else {
- spec->pin_configs = stac9200_brd_tbl[spec->board_config];
- stac92xx_set_config_regs(codec);
+ } else
+ err = stac_save_pin_cfgs(codec,
+ stac9200_brd_tbl[spec->board_config]);
+ if (err < 0) {
+ stac92xx_free(codec);
+ return err;
}
spec->multiout.max_channels = 2;
@@ -4094,14 +4277,12 @@ static int patch_stac925x(struct hda_codec *codec)
snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC925x,"
"using BIOS defaults\n");
err = stac92xx_save_bios_config_regs(codec);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
- spec->pin_configs = spec->bios_pin_configs;
- } else if (stac925x_brd_tbl[spec->board_config] != NULL){
- spec->pin_configs = stac925x_brd_tbl[spec->board_config];
- stac92xx_set_config_regs(codec);
+ } else
+ err = stac_save_pin_cfgs(codec,
+ stac925x_brd_tbl[spec->board_config]);
+ if (err < 0) {
+ stac92xx_free(codec);
+ return err;
}
spec->multiout.max_channels = 2;
@@ -4183,14 +4364,12 @@ again:
snd_printdd(KERN_INFO "hda_codec: Unknown model for"
" STAC92HD73XX, using BIOS defaults\n");
err = stac92xx_save_bios_config_regs(codec);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
- spec->pin_configs = spec->bios_pin_configs;
- } else {
- spec->pin_configs = stac92hd73xx_brd_tbl[spec->board_config];
- stac92xx_set_config_regs(codec);
+ } else
+ err = stac_save_pin_cfgs(codec,
+ stac92hd73xx_brd_tbl[spec->board_config]);
+ if (err < 0) {
+ stac92xx_free(codec);
+ return err;
}
spec->multiout.num_dacs = snd_hda_get_connections(codec, 0x0a,
@@ -4243,6 +4422,7 @@ again:
spec->num_smuxes = 0;
spec->mixer = &stac92hd73xx_6ch_mixer[DELL_M6_MIXER];
spec->amp_nids = &stac92hd73xx_amp_nids[DELL_M6_AMP];
+ spec->eapd_switch = 0;
spec->num_amps = 1;
if (!spec->init)
@@ -4274,6 +4454,7 @@ again:
default:
spec->num_dmics = STAC92HD73XX_NUM_DMICS;
spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids);
+ spec->eapd_switch = 1;
}
if (spec->board_config > STAC_92HD73XX_REF) {
/* GPIO0 High = Enable EAPD */
@@ -4364,14 +4545,12 @@ again:
snd_printdd(KERN_INFO "hda_codec: Unknown model for"
" STAC92HD83XXX, using BIOS defaults\n");
err = stac92xx_save_bios_config_regs(codec);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
- spec->pin_configs = spec->bios_pin_configs;
- } else {
- spec->pin_configs = stac92hd83xxx_brd_tbl[spec->board_config];
- stac92xx_set_config_regs(codec);
+ } else
+ err = stac_save_pin_cfgs(codec,
+ stac92hd83xxx_brd_tbl[spec->board_config]);
+ if (err < 0) {
+ stac92xx_free(codec);
+ return err;
}
err = stac92xx_parse_auto_config(codec, 0x1d, 0);
@@ -4419,7 +4598,13 @@ static int stac92hd71xx_resume(struct hda_codec *codec)
static int stac92hd71xx_suspend(struct hda_codec *codec, pm_message_t state)
{
+ struct sigmatel_spec *spec = codec->spec;
+
stac92hd71xx_set_power_state(codec, AC_PWRST_D3);
+ if (spec->eapd_mask)
+ stac_gpio_set(codec, spec->gpio_mask,
+ spec->gpio_dir, spec->gpio_data &
+ ~spec->eapd_mask);
return 0;
};
@@ -4472,14 +4657,12 @@ again:
snd_printdd(KERN_INFO "hda_codec: Unknown model for"
" STAC92HD71BXX, using BIOS defaults\n");
err = stac92xx_save_bios_config_regs(codec);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
- spec->pin_configs = spec->bios_pin_configs;
- } else {
- spec->pin_configs = stac92hd71bxx_brd_tbl[spec->board_config];
- stac92xx_set_config_regs(codec);
+ } else
+ err = stac_save_pin_cfgs(codec,
+ stac92hd71bxx_brd_tbl[spec->board_config]);
+ if (err < 0) {
+ stac92xx_free(codec);
+ return err;
}
if (spec->board_config > STAC_92HD71BXX_REF) {
@@ -4505,8 +4688,11 @@ again:
snd_hda_codec_write_cache(codec, codec->afg, 0,
AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02);
snd_hda_codec_write_cache(codec, codec->afg, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- (AC_USRSP_EN | STAC_VREF_EVENT | 0x01));
+ AC_VERB_SET_UNSOLICITED_ENABLE,
+ (AC_USRSP_EN | STAC_VREF_EVENT | codec->afg));
+ err = stac92xx_add_event(spec, codec->afg, 0x02);
+ if (err < 0)
+ return err;
spec->gpio_mask |= 0x02;
break;
}
@@ -4525,7 +4711,7 @@ again:
/* disable VSW */
spec->init = &stac92hd71bxx_analog_core_init[HD_DISABLE_PORTF];
- stac92xx_set_config_reg(codec, 0xf, 0x40f000f0);
+ stac_change_pin_config(codec, 0xf, 0x40f000f0);
break;
case 0x111d7603: /* 6 Port with Analog Mixer */
if ((codec->revision_id & 0xf) == 1) {
@@ -4567,7 +4753,7 @@ again:
spec->num_dmuxes = 0;
/* enable internal microphone */
- stac92xx_set_config_reg(codec, 0x0e, 0x01813040);
+ stac_change_pin_config(codec, 0x0e, 0x01813040);
stac92xx_auto_set_pinctl(codec, 0x0e,
AC_PINCTL_IN_EN | AC_PINCTL_VREF_80);
break;
@@ -4662,14 +4848,12 @@ static int patch_stac922x(struct hda_codec *codec)
snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, "
"using BIOS defaults\n");
err = stac92xx_save_bios_config_regs(codec);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
- spec->pin_configs = spec->bios_pin_configs;
- } else if (stac922x_brd_tbl[spec->board_config] != NULL) {
- spec->pin_configs = stac922x_brd_tbl[spec->board_config];
- stac92xx_set_config_regs(codec);
+ } else
+ err = stac_save_pin_cfgs(codec,
+ stac922x_brd_tbl[spec->board_config]);
+ if (err < 0) {
+ stac92xx_free(codec);
+ return err;
}
spec->adc_nids = stac922x_adc_nids;
@@ -4732,14 +4916,12 @@ static int patch_stac927x(struct hda_codec *codec)
snd_printdd(KERN_INFO "hda_codec: Unknown model for"
"STAC927x, using BIOS defaults\n");
err = stac92xx_save_bios_config_regs(codec);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
- spec->pin_configs = spec->bios_pin_configs;
- } else {
- spec->pin_configs = stac927x_brd_tbl[spec->board_config];
- stac92xx_set_config_regs(codec);
+ } else
+ err = stac_save_pin_cfgs(codec,
+ stac927x_brd_tbl[spec->board_config]);
+ if (err < 0) {
+ stac92xx_free(codec);
+ return err;
}
spec->digbeep_nid = 0x23;
@@ -4769,15 +4951,15 @@ static int patch_stac927x(struct hda_codec *codec)
case 0x10280209:
case 0x1028022e:
/* correct the device field to SPDIF out */
- stac92xx_set_config_reg(codec, 0x21, 0x01442070);
+ stac_change_pin_config(codec, 0x21, 0x01442070);
break;
};
/* configure the analog microphone on some laptops */
- stac92xx_set_config_reg(codec, 0x0c, 0x90a79130);
+ stac_change_pin_config(codec, 0x0c, 0x90a79130);
/* correct the front output jack as a hp out */
- stac92xx_set_config_reg(codec, 0x0f, 0x0227011f);
+ stac_change_pin_config(codec, 0x0f, 0x0227011f);
/* correct the front input jack as a mic */
- stac92xx_set_config_reg(codec, 0x0e, 0x02a79130);
+ stac_change_pin_config(codec, 0x0e, 0x02a79130);
/* fallthru */
case STAC_DELL_3ST:
/* GPIO2 High = Enable EAPD */
@@ -4806,6 +4988,7 @@ static int patch_stac927x(struct hda_codec *codec)
spec->num_pwrs = 0;
spec->aloopback_mask = 0x40;
spec->aloopback_shift = 0;
+ spec->eapd_switch = 1;
err = stac92xx_parse_auto_config(codec, 0x1e, 0x20);
if (!err) {
@@ -4858,14 +5041,12 @@ static int patch_stac9205(struct hda_codec *codec)
if (spec->board_config < 0) {
snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9205, using BIOS defaults\n");
err = stac92xx_save_bios_config_regs(codec);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
- spec->pin_configs = spec->bios_pin_configs;
- } else {
- spec->pin_configs = stac9205_brd_tbl[spec->board_config];
- stac92xx_set_config_regs(codec);
+ } else
+ err = stac_save_pin_cfgs(codec,
+ stac9205_brd_tbl[spec->board_config]);
+ if (err < 0) {
+ stac92xx_free(codec);
+ return err;
}
spec->digbeep_nid = 0x23;
@@ -4886,20 +5067,24 @@ static int patch_stac9205(struct hda_codec *codec)
spec->aloopback_mask = 0x40;
spec->aloopback_shift = 0;
+ spec->eapd_switch = 1;
spec->multiout.dac_nids = spec->dac_nids;
switch (spec->board_config){
case STAC_9205_DELL_M43:
/* Enable SPDIF in/out */
- stac92xx_set_config_reg(codec, 0x1f, 0x01441030);
- stac92xx_set_config_reg(codec, 0x20, 0x1c410030);
+ stac_change_pin_config(codec, 0x1f, 0x01441030);
+ stac_change_pin_config(codec, 0x20, 0x1c410030);
/* Enable unsol response for GPIO4/Dock HP connection */
snd_hda_codec_write_cache(codec, codec->afg, 0,
AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10);
snd_hda_codec_write_cache(codec, codec->afg, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- (AC_USRSP_EN | STAC_HP_EVENT));
+ AC_VERB_SET_UNSOLICITED_ENABLE,
+ (AC_USRSP_EN | STAC_VREF_EVENT | codec->afg));
+ err = stac92xx_add_event(spec, codec->afg, 0x01);
+ if (err < 0)
+ return err;
spec->gpio_dir = 0x0b;
spec->eapd_mask = 0x01;
@@ -4993,29 +5178,11 @@ static struct hda_verb vaio_ar_init[] = {
{}
};
-/* bind volumes of both NID 0x02 and 0x05 */
-static struct hda_bind_ctls vaio_bind_master_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-/* bind volumes of both NID 0x02 and 0x05 */
-static struct hda_bind_ctls vaio_bind_master_sw = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
- 0,
- },
-};
-
static struct snd_kcontrol_new vaio_mixer[] = {
- HDA_BIND_VOL("Master Playback Volume", &vaio_bind_master_vol),
- HDA_BIND_SW("Master Playback Switch", &vaio_bind_master_sw),
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x02, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x02, 0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x05, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x05, 0, HDA_OUTPUT),
/* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */
HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT),
HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT),
@@ -5031,8 +5198,10 @@ static struct snd_kcontrol_new vaio_mixer[] = {
};
static struct snd_kcontrol_new vaio_ar_mixer[] = {
- HDA_BIND_VOL("Master Playback Volume", &vaio_bind_master_vol),
- HDA_BIND_SW("Master Playback Switch", &vaio_bind_master_sw),
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x02, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x02, 0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x05, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x05, 0, HDA_OUTPUT),
/* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */
HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT),
HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT),
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index 63e4871e5d8f..05182be1c9f0 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -53,9 +53,6 @@
#define AMP_VAL_IDX_SHIFT 19
#define AMP_VAL_IDX_MASK (0x0f<<19)
-#define NUM_CONTROL_ALLOC 32
-#define NUM_VERB_ALLOC 32
-
/* Pin Widget NID */
#define VT1708_HP_NID 0x13
#define VT1708_DIGOUT_NID 0x14
@@ -145,8 +142,6 @@ enum {
AUTO_SEQ_SIDE
};
-#define get_amp_nid(kc) ((kc)->private_value & 0xffff)
-
/* Some VT1708S based boards gets the micboost setting wrong, so we have
* to apply some brute-force and re-write the TLV's by software. */
static int mic_boost_tlv(struct snd_kcontrol *kcontrol, int op_flag,
@@ -227,8 +222,7 @@ struct via_spec {
/* dynamic controls, init_verbs and input_mux */
struct auto_pin_cfg autocfg;
- unsigned int num_kctl_alloc, num_kctl_used;
- struct snd_kcontrol_new *kctl_alloc;
+ struct snd_array kctls;
struct hda_input_mux private_imux[2];
hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
@@ -272,33 +266,31 @@ static int via_add_control(struct via_spec *spec, int type, const char *name,
{
struct snd_kcontrol_new *knew;
- if (spec->num_kctl_used >= spec->num_kctl_alloc) {
- int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
-
- /* array + terminator */
- knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL);
- if (!knew)
- return -ENOMEM;
- if (spec->kctl_alloc) {
- memcpy(knew, spec->kctl_alloc,
- sizeof(*knew) * spec->num_kctl_alloc);
- kfree(spec->kctl_alloc);
- }
- spec->kctl_alloc = knew;
- spec->num_kctl_alloc = num;
- }
-
- knew = &spec->kctl_alloc[spec->num_kctl_used];
+ snd_array_init(&spec->kctls, sizeof(*knew), 32);
+ knew = snd_array_new(&spec->kctls);
+ if (!knew)
+ return -ENOMEM;
*knew = vt1708_control_templates[type];
knew->name = kstrdup(name, GFP_KERNEL);
-
if (!knew->name)
return -ENOMEM;
knew->private_value = val;
- spec->num_kctl_used++;
return 0;
}
+static void via_free_kctls(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+
+ if (spec->kctls.list) {
+ struct snd_kcontrol_new *kctl = spec->kctls.list;
+ int i;
+ for (i = 0; i < spec->kctls.used; i++)
+ kfree(kctl[i].name);
+ }
+ snd_array_free(&spec->kctls);
+}
+
/* create input playback/capture controls for the given pin */
static int via_new_analog_input(struct via_spec *spec, hda_nid_t pin,
const char *ctlname, int idx, int mix_nid)
@@ -896,6 +888,7 @@ static int via_build_controls(struct hda_codec *codec)
if (err < 0)
return err;
}
+ via_free_kctls(codec); /* no longer needed */
return 0;
}
@@ -941,17 +934,11 @@ static int via_build_pcms(struct hda_codec *codec)
static void via_free(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
- unsigned int i;
if (!spec)
return;
- if (spec->kctl_alloc) {
- for (i = 0; i < spec->num_kctl_used; i++)
- kfree(spec->kctl_alloc[i].name);
- kfree(spec->kctl_alloc);
- }
-
+ via_free_kctls(codec);
kfree(codec->spec);
}
@@ -1373,8 +1360,8 @@ static int vt1708_parse_auto_config(struct hda_codec *codec)
if (spec->autocfg.dig_in_pin)
spec->dig_in_nid = VT1708_DIGIN_NID;
- if (spec->kctl_alloc)
- spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->kctls.list)
+ spec->mixers[spec->num_mixers++] = spec->kctls.list;
spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs;
@@ -1846,8 +1833,8 @@ static int vt1709_parse_auto_config(struct hda_codec *codec)
if (spec->autocfg.dig_in_pin)
spec->dig_in_nid = VT1709_DIGIN_NID;
- if (spec->kctl_alloc)
- spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->kctls.list)
+ spec->mixers[spec->num_mixers++] = spec->kctls.list;
spec->input_mux = &spec->private_imux[0];
@@ -2390,8 +2377,8 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec)
if (spec->autocfg.dig_in_pin)
spec->dig_in_nid = VT1708B_DIGIN_NID;
- if (spec->kctl_alloc)
- spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->kctls.list)
+ spec->mixers[spec->num_mixers++] = spec->kctls.list;
spec->input_mux = &spec->private_imux[0];
@@ -2855,8 +2842,8 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec)
spec->extra_dig_out_nid = 0x15;
- if (spec->kctl_alloc)
- spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->kctls.list)
+ spec->mixers[spec->num_mixers++] = spec->kctls.list;
spec->input_mux = &spec->private_imux[0];
@@ -3174,8 +3161,8 @@ static int vt1702_parse_auto_config(struct hda_codec *codec)
spec->extra_dig_out_nid = 0x1B;
- if (spec->kctl_alloc)
- spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->kctls.list)
+ spec->mixers[spec->num_mixers++] = spec->kctls.list;
spec->input_mux = &spec->private_imux[0];