summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2008-10-27 08:36:48 +0100
committerTakashi Iwai <tiwai@suse.de>2008-10-27 08:36:48 +0100
commit42dc2378b52bb6e155dd2e4fb4ca694785bf2c1d (patch)
treecba90396b1c91c923758b47d55ad0f1d50100168
parentda74ae3e32374755e0fbdfed4074cf839a82f615 (diff)
parent74aeaabc3e452b29bc1b9eac5aa48923569f8a4e (diff)
downloadblackbird-op-linux-42dc2378b52bb6e155dd2e4fb4ca694785bf2c1d.tar.gz
blackbird-op-linux-42dc2378b52bb6e155dd2e4fb4ca694785bf2c1d.zip
Merge branch 'topic/hda-next' into topic/hda
-rw-r--r--include/linux/input.h1
-rw-r--r--include/sound/jack.h1
-rw-r--r--sound/core/jack.c9
-rw-r--r--sound/pci/Kconfig1
-rw-r--r--sound/pci/hda/hda_codec.c428
-rw-r--r--sound/pci/hda/hda_codec.h46
-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.c121
-rw-r--r--sound/pci/hda/hda_local.h10
-rw-r--r--sound/pci/hda/hda_proc.c44
-rw-r--r--sound/pci/hda/patch_analog.c56
-rw-r--r--sound/pci/hda/patch_conexant.c11
-rw-r--r--sound/pci/hda/patch_realtek.c86
-rw-r--r--sound/pci/hda/patch_sigmatel.c249
-rw-r--r--sound/pci/hda/patch_via.c71
16 files changed, 970 insertions, 412 deletions
diff --git a/include/linux/input.h b/include/linux/input.h
index a5802c9c81a4..7323d2ff5151 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -644,6 +644,7 @@ struct input_absinfo {
#define SW_RADIO SW_RFKILL_ALL /* deprecated */
#define SW_MICROPHONE_INSERT 0x04 /* set = inserted */
#define SW_DOCK 0x05 /* set = plugged into dock */
+#define SW_LINEOUT_INSERT 0x06 /* set = inserted */
#define SW_MAX 0x0f
#define SW_CNT (SW_MAX+1)
diff --git a/include/sound/jack.h b/include/sound/jack.h
index b1b2b8b59adb..7cb25f4b50bb 100644
--- a/include/sound/jack.h
+++ b/include/sound/jack.h
@@ -35,6 +35,7 @@ enum snd_jack_types {
SND_JACK_HEADPHONE = 0x0001,
SND_JACK_MICROPHONE = 0x0002,
SND_JACK_HEADSET = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE,
+ SND_JACK_LINEOUT = 0x0004,
};
struct snd_jack {
diff --git a/sound/core/jack.c b/sound/core/jack.c
index 8133a2b173a5..438445f77d6d 100644
--- a/sound/core/jack.c
+++ b/sound/core/jack.c
@@ -34,6 +34,7 @@ static int snd_jack_dev_free(struct snd_device *device)
else
input_free_device(jack->input_dev);
+ kfree(jack->id);
kfree(jack);
return 0;
@@ -87,7 +88,7 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
if (jack == NULL)
return -ENOMEM;
- jack->id = id;
+ jack->id = kstrdup(id, GFP_KERNEL);
jack->input_dev = input_allocate_device();
if (jack->input_dev == NULL) {
@@ -102,6 +103,9 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
if (type & SND_JACK_HEADPHONE)
input_set_capability(jack->input_dev, EV_SW,
SW_HEADPHONE_INSERT);
+ if (type & SND_JACK_LINEOUT)
+ input_set_capability(jack->input_dev, EV_SW,
+ SW_LINEOUT_INSERT);
if (type & SND_JACK_MICROPHONE)
input_set_capability(jack->input_dev, EV_SW,
SW_MICROPHONE_INSERT);
@@ -150,6 +154,9 @@ void snd_jack_report(struct snd_jack *jack, int status)
if (jack->type & SND_JACK_HEADPHONE)
input_report_switch(jack->input_dev, SW_HEADPHONE_INSERT,
status & SND_JACK_HEADPHONE);
+ if (jack->type & SND_JACK_LINEOUT)
+ input_report_switch(jack->input_dev, SW_LINEOUT_INSERT,
+ status & SND_JACK_LINEOUT);
if (jack->type & SND_JACK_MICROPHONE)
input_report_switch(jack->input_dev, SW_MICROPHONE_INSERT,
status & SND_JACK_MICROPHONE);
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index 7003711f4fcc..7e408908b755 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.
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 6447754ae56e..eaa8b5676eae 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -107,6 +107,52 @@ 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];
+}
+
/**
* snd_hda_codec_read - send a command and get the response
* @codec: the HDA codec
@@ -344,7 +390,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;
@@ -393,6 +439,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
@@ -408,6 +468,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,
};
@@ -446,7 +507,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
@@ -454,7 +515,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;
@@ -481,15 +542,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) {
@@ -502,10 +562,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;
}
/*
@@ -570,11 +635,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);
}
@@ -616,6 +684,14 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
mutex_init(&codec->spdif_mutex);
init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
+ snd_array_init(&codec->mixers, sizeof(struct snd_kcontrol *), 32);
+ if (codec->bus->modelname) {
+ codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
+ if (!codec->modelname) {
+ snd_hda_codec_free(codec);
+ return -ENODEV;
+ }
+ }
#ifdef CONFIG_SND_HDA_POWER_SAVE
INIT_DELAYED_WORK(&codec->power_work, hda_power_work);
@@ -661,12 +737,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);
@@ -683,25 +788,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;
}
/**
@@ -756,12 +845,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. */
@@ -770,38 +859,18 @@ static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache,
{
u16 idx = key % (u16)ARRAY_SIZE(cache->hash);
u16 cur = cache->hash[idx];
+ struct hda_cache_head *info_head = cache->buf.list;
struct hda_cache_head *info;
while (cur != 0xffff) {
- info = (struct hda_cache_head *)(cache->buffer +
- cur * cache->record_size);
+ info = &info_head[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);
info->key = key;
info->val = 0;
info->next = cache->hash[idx];
@@ -942,10 +1011,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;
@@ -1097,6 +1166,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)
@@ -1114,7 +1234,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;
@@ -1578,7 +1698,7 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
kctl = snd_ctl_new1(dig_mix, codec);
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;
}
@@ -1622,7 +1742,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));
}
@@ -1724,7 +1844,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;
}
@@ -1779,10 +1899,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;
@@ -1867,6 +1987,17 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
}
}
+#ifdef CONFIG_SND_HDA_HWDEP
+/* execute additional init verbs */
+static void hda_exec_init_verbs(struct hda_codec *codec)
+{
+ if (codec->init_verbs.list)
+ snd_hda_sequence_write(codec, codec->init_verbs.list);
+}
+#else
+static inline void hda_exec_init_verbs(struct hda_codec *codec) {}
+#endif
+
#ifdef SND_HDA_NEEDS_RESUME
/*
* call suspend and power-down; used both from PM and power-save
@@ -1893,6 +2024,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 {
@@ -1918,23 +2050,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;
}
@@ -2235,8 +2375,8 @@ static int hda_pcm_default_cleanup(struct hda_pcm_stream *hinfo,
return 0;
}
-static int __devinit set_pcm_default_values(struct hda_codec *codec,
- struct hda_pcm_stream *info)
+static int set_pcm_default_values(struct hda_codec *codec,
+ struct hda_pcm_stream *info)
{
/* query support PCM information from the given NID */
if (info->nid && (!info->rates || !info->formats)) {
@@ -2262,6 +2402,28 @@ 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_pcm_stream *info;
+ int stream, err;
+
+ if (!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 codec->bus->ops.attach_pcm(codec, pcm);
+}
+
/**
* snd_hda_build_pcms - build PCM information
* @bus: the BUS
@@ -2288,25 +2450,67 @@ 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;
+ 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;
}
@@ -2332,11 +2536,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;
@@ -2389,7 +2593,7 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
kctl = snd_ctl_new1(knew, codec);
if (!kctl)
return -ENOMEM;
- err = snd_ctl_add(codec->bus->card, kctl);
+ err = snd_hda_ctl_add(codec, kctl);
if (err < 0) {
if (!codec->addr)
return err;
@@ -2397,7 +2601,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;
}
@@ -3138,3 +3342,37 @@ 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 = 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 array->list + (array->used++ * array->elem_size);
+}
+
+/* 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..c5f91c918d19 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -520,6 +520,26 @@ 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;
+}
+
+/*
* Structures
*/
@@ -542,6 +562,8 @@ struct hda_bus_ops {
unsigned int (*get_response)(struct hda_codec *codec);
/* free the private data */
void (*private_free)(struct hda_bus *);
+ /* attach a PCM stream */
+ int (*attach_pcm)(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);
@@ -635,10 +657,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 +699,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 +719,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 +740,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 +751,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 +827,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 +860,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_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 9f316c1b2790..7b0abf08a583 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -1180,6 +1180,7 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
return 0;
}
+static int azx_attach_pcm_stream(struct hda_codec *codec, struct hda_pcm *cpcm);
/*
* Codec initialization
@@ -1212,6 +1213,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
@@ -1718,111 +1720,58 @@ 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_codec *codec, struct hda_pcm *cpcm)
{
- int err;
+ struct azx *chip = codec->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;
}
@@ -2324,7 +2273,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;
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 7957fefda730..4a08c31b498a 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
@@ -393,10 +395,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
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
index 743d77922bce..b36d4d06485d 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
@@ -145,32 +145,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 +180,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 +241,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));
@@ -511,12 +478,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 2b00c4afdf97..02643bce5634 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_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_realtek.c b/sound/pci/hda/patch_realtek.c
index ef4955c73c88..fb8fae9f702d 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -284,8 +284,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];
@@ -1625,6 +1624,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;
@@ -1671,6 +1673,7 @@ static int alc_build_controls(struct hda_codec *codec)
return err;
}
+ alc_free_kctls(codec); /* no longer needed */
return 0;
}
@@ -2761,19 +2764,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 */
}
@@ -3458,9 +3469,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,
@@ -3478,29 +3486,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;
}
@@ -3824,8 +3818,8 @@ 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)
+ spec->mixers[spec->num_mixers++] = spec->kctls.list;
spec->init_verbs[spec->num_init_verbs++] = alc880_volume_init_verbs;
@@ -5218,7 +5212,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)
@@ -5228,8 +5222,8 @@ 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)
+ spec->mixers[spec->num_mixers++] = spec->kctls.list;
spec->init_verbs[spec->num_init_verbs++] = alc260_volume_init_verbs;
@@ -10302,8 +10296,8 @@ 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)
+ spec->mixers[spec->num_mixers++] = spec->kctls.list;
spec->init_verbs[spec->num_init_verbs++] = alc262_volume_init_verbs;
spec->num_mux_defs = 1;
@@ -11433,8 +11427,8 @@ 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)
+ spec->mixers[spec->num_mixers++] = spec->kctls.list;
if (spec->autocfg.speaker_pins[0] != 0x1d)
spec->mixers[spec->num_mixers++] = alc268_beep_mixer;
@@ -12205,8 +12199,8 @@ 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)
+ spec->mixers[spec->num_mixers++] = 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++)
@@ -13303,8 +13297,8 @@ 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)
+ spec->mixers[spec->num_mixers++] = spec->kctls.list;
spec->init_verbs[spec->num_init_verbs++] = alc861_auto_init_verbs;
@@ -14414,8 +14408,8 @@ 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)
+ spec->mixers[spec->num_mixers++] = spec->kctls.list;
spec->init_verbs[spec->num_init_verbs++]
= alc861vd_volume_init_verbs;
@@ -16241,8 +16235,8 @@ 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)
+ spec->mixers[spec->num_mixers++] = spec->kctls.list;
spec->num_mux_defs = 1;
spec->input_mux = &spec->private_imux;
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 788fdc6f3264..c2f900f39466 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -30,12 +30,13 @@
#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_INSERT_EVENT 0x10
#define STAC_PWR_EVENT 0x20
#define STAC_HP_EVENT 0x30
#define STAC_VREF_EVENT 0x40
@@ -129,6 +130,17 @@ 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;
@@ -161,6 +173,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;
@@ -218,8 +236,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;
@@ -1233,6 +1250,8 @@ static const char *slave_sws[] = {
NULL
};
+static void stac92xx_free_kctls(struct hda_codec *codec);
+
static int stac92xx_build_controls(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
@@ -1250,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;
@@ -1305,6 +1324,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
return err;
}
+ stac92xx_free_kctls(codec); /* no longer needed */
return 0;
}
@@ -2453,13 +2473,15 @@ static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int nid = cfg->hp_pins[cfg->hp_outs - 1];
spec->hp_switch = ucontrol->value.integer.value[0];
/* check to be sure that the ports are upto date with
* switch changes
*/
- codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
+ codec->patch_ops.unsol_event(codec, (STAC_HP_EVENT | nid) << 26);
return 1;
}
@@ -2499,7 +2521,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;
}
@@ -2592,28 +2615,16 @@ static int stac92xx_add_control_idx(struct sigmatel_spec *spec, int type,
{
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 = stac92xx_control_templates[type];
knew->index = idx;
knew->name = kstrdup(name, GFP_KERNEL);
if (! knew->name)
return -ENOMEM;
knew->private_value = val;
- spec->num_kctl_used++;
return 0;
}
@@ -3434,8 +3445,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;
@@ -3536,8 +3547,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;
@@ -3581,13 +3592,70 @@ 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)
+{
+ 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);
+}
+
+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)
@@ -3617,7 +3685,7 @@ static int stac92xx_init(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
+ int i, err;
snd_hda_sequence_write(codec, spec->init);
@@ -3630,21 +3698,36 @@ 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++) {
+ int type = SND_JACK_HEADPHONE;
+ hda_nid_t nid = cfg->hp_pins[i];
+ enable_pin_detect(codec, nid, STAC_HP_EVENT | nid);
+ /* jack detection */
+ if (cfg->hp_outs == i)
+ type |= SND_JACK_LINEOUT;
+ err = stac92xx_add_jack(codec, nid, type);
+ if (err < 0)
+ return err;
+
+ }
/* 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);
}
+ 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++) {
hda_nid_t nid = cfg->input_pins[i];
if (nid) {
@@ -3657,6 +3740,11 @@ static int stac92xx_init(struct hda_codec *codec)
if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC)
pinctl |= stac92xx_get_vref(codec, nid);
stac92xx_auto_set_pinctl(codec, nid, pinctl);
+ err = stac92xx_add_jack(codec, nid,
+ SND_JACK_MICROPHONE);
+ if (err < 0)
+ return err;
+ enable_pin_detect(codec, nid, STAC_INSERT_EVENT | nid);
}
}
for (i = 0; i < spec->num_dmics; i++)
@@ -3698,22 +3786,42 @@ static int stac92xx_init(struct hda_codec *codec)
return 0;
}
+static void stac92xx_free_jacks(struct hda_codec *codec)
+{
+ 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);
+}
+
+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);
+ stac92xx_free_jacks(codec);
+ snd_array_free(&spec->events);
kfree(spec);
snd_hda_detach_beep_device(codec);
@@ -3852,24 +3960,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)));
@@ -4392,8 +4533,11 @@ again:
snd_hda_codec_write(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;
}
@@ -4792,8 +4936,11 @@ static int patch_stac9205(struct hda_codec *codec)
snd_hda_codec_write(codec, codec->afg, 0,
AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10);
snd_hda_codec_write_cache(codec, codec->afg, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- (AC_USRSP_EN | STAC_HP_EVENT));
+ 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;
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index 63e4871e5d8f..760e14ae3bff 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
@@ -227,8 +224,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 +268,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 +890,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 +936,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 +1362,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 +1835,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 +2379,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 +2844,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 +3163,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];
OpenPOWER on IntegriCloud