|
@@ -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) {}
|
|
static inline void hda_keep_power_on(struct hda_codec *codec) {}
|
|
#endif
|
|
#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
|
|
* snd_hda_codec_read - send a command and get the response
|
|
* @codec: the HDA codec
|
|
* @codec: the HDA codec
|
|
@@ -344,7 +390,7 @@ static void process_unsol_events(struct work_struct *work)
|
|
/*
|
|
/*
|
|
* initialize unsolicited queue
|
|
* 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;
|
|
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);
|
|
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
|
|
* snd_hda_bus_new - create a HDA bus
|
|
* @card: the card entry
|
|
* @card: the card entry
|
|
@@ -408,6 +468,7 @@ int __devinit snd_hda_bus_new(struct snd_card *card,
|
|
struct hda_bus *bus;
|
|
struct hda_bus *bus;
|
|
int err;
|
|
int err;
|
|
static struct snd_device_ops dev_ops = {
|
|
static struct snd_device_ops dev_ops = {
|
|
|
|
+ .dev_register = snd_hda_bus_dev_register,
|
|
.dev_free = snd_hda_bus_dev_free,
|
|
.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
|
|
#ifdef CONFIG_SND_HDA_GENERIC
|
|
#define is_generic_config(codec) \
|
|
#define is_generic_config(codec) \
|
|
- (codec->bus->modelname && !strcmp(codec->bus->modelname, "generic"))
|
|
|
|
|
|
+ (codec->modelname && !strcmp(codec->modelname, "generic"))
|
|
#else
|
|
#else
|
|
#define is_generic_config(codec) 0
|
|
#define is_generic_config(codec) 0
|
|
#endif
|
|
#endif
|
|
@@ -454,7 +515,7 @@ int __devinit snd_hda_bus_new(struct snd_card *card,
|
|
/*
|
|
/*
|
|
* find a matching codec preset
|
|
* 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)
|
|
find_codec_preset(struct hda_codec *codec)
|
|
{
|
|
{
|
|
const struct hda_codec_preset **tbl, *preset;
|
|
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 struct hda_vendor_id *c;
|
|
const char *vendor = NULL;
|
|
const char *vendor = NULL;
|
|
u16 vendor_id = codec->vendor_id >> 16;
|
|
u16 vendor_id = codec->vendor_id >> 16;
|
|
- char tmp[16];
|
|
|
|
|
|
+ char tmp[16], name[32];
|
|
|
|
|
|
for (c = hda_vendor_ids; c->id; c++) {
|
|
for (c = hda_vendor_ids; c->id; c++) {
|
|
if (c->id == vendor_id) {
|
|
if (c->id == vendor_id) {
|
|
@@ -502,10 +562,15 @@ void snd_hda_get_codec_name(struct hda_codec *codec,
|
|
vendor = tmp;
|
|
vendor = tmp;
|
|
}
|
|
}
|
|
if (codec->preset && codec->preset->name)
|
|
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
|
|
else
|
|
- snprintf(name, namelen, "%s ID %x", vendor,
|
|
|
|
|
|
+ snprintf(name, sizeof(name), "%s ID %x", vendor,
|
|
codec->vendor_id & 0xffff);
|
|
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();
|
|
flush_scheduled_work();
|
|
#endif
|
|
#endif
|
|
list_del(&codec->list);
|
|
list_del(&codec->list);
|
|
|
|
+ snd_array_free(&codec->mixers);
|
|
codec->bus->caddr_tbl[codec->addr] = NULL;
|
|
codec->bus->caddr_tbl[codec->addr] = NULL;
|
|
if (codec->patch_ops.free)
|
|
if (codec->patch_ops.free)
|
|
codec->patch_ops.free(codec);
|
|
codec->patch_ops.free(codec);
|
|
free_hda_cache(&codec->amp_cache);
|
|
free_hda_cache(&codec->amp_cache);
|
|
free_hda_cache(&codec->cmd_cache);
|
|
free_hda_cache(&codec->cmd_cache);
|
|
|
|
+ kfree(codec->name);
|
|
|
|
+ kfree(codec->modelname);
|
|
kfree(codec->wcaps);
|
|
kfree(codec->wcaps);
|
|
kfree(codec);
|
|
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);
|
|
mutex_init(&codec->spdif_mutex);
|
|
init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
|
|
init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
|
|
init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
|
|
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
|
|
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
|
INIT_DELAYED_WORK(&codec->power_work, hda_power_work);
|
|
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,
|
|
snd_hda_codec_read(codec, nid, 0,
|
|
AC_VERB_GET_SUBSYSTEM_ID, 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);
|
|
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 */
|
|
/* 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)) {
|
|
if (is_generic_config(codec)) {
|
|
err = snd_hda_parse_generic_codec(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");
|
|
printk(KERN_ERR "hda-codec: No codec parser is available\n");
|
|
|
|
|
|
patched:
|
|
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, 0, sizeof(*cache));
|
|
memset(cache->hash, 0xff, sizeof(cache->hash));
|
|
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)
|
|
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. */
|
|
/* 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 idx = key % (u16)ARRAY_SIZE(cache->hash);
|
|
u16 cur = cache->hash[idx];
|
|
u16 cur = cache->hash[idx];
|
|
|
|
+ struct hda_cache_head *info_head = cache->buf.list;
|
|
struct hda_cache_head *info;
|
|
struct hda_cache_head *info;
|
|
|
|
|
|
while (cur != 0xffff) {
|
|
while (cur != 0xffff) {
|
|
- info = (struct hda_cache_head *)(cache->buffer +
|
|
|
|
- cur * cache->record_size);
|
|
|
|
|
|
+ info = &info_head[cur];
|
|
if (info->key == key)
|
|
if (info->key == key)
|
|
return info;
|
|
return info;
|
|
cur = info->next;
|
|
cur = info->next;
|
|
}
|
|
}
|
|
|
|
|
|
/* add a new hash entry */
|
|
/* 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->key = key;
|
|
info->val = 0;
|
|
info->val = 0;
|
|
info->next = cache->hash[idx];
|
|
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 */
|
|
/* resume the all amp commands from the cache */
|
|
void snd_hda_codec_resume_amp(struct hda_codec *codec)
|
|
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;
|
|
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;
|
|
u32 key = buffer->head.key;
|
|
hda_nid_t nid;
|
|
hda_nid_t nid;
|
|
unsigned int idx, dir, ch;
|
|
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);
|
|
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 */
|
|
/* create a virtual master control and add slaves */
|
|
int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
|
|
int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
|
|
unsigned int *tlv, const char **slaves)
|
|
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);
|
|
kctl = snd_ctl_make_virtual_master(name, tlv);
|
|
if (!kctl)
|
|
if (!kctl)
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
- err = snd_ctl_add(codec->bus->card, kctl);
|
|
|
|
|
|
+ err = snd_hda_ctl_add(codec, kctl);
|
|
if (err < 0)
|
|
if (err < 0)
|
|
return err;
|
|
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 = snd_ctl_new1(dig_mix, codec);
|
|
kctl->id.index = idx;
|
|
kctl->id.index = idx;
|
|
kctl->private_value = nid;
|
|
kctl->private_value = nid;
|
|
- err = snd_ctl_add(codec->bus->card, kctl);
|
|
|
|
|
|
+ err = snd_hda_ctl_add(codec, kctl);
|
|
if (err < 0)
|
|
if (err < 0)
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
@@ -1622,7 +1742,7 @@ int snd_hda_create_spdif_share_sw(struct hda_codec *codec,
|
|
if (!mout->dig_out_nid)
|
|
if (!mout->dig_out_nid)
|
|
return 0;
|
|
return 0;
|
|
/* ATTENTION: here mout is passed as private_data, instead of codec */
|
|
/* 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));
|
|
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++) {
|
|
for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) {
|
|
kctl = snd_ctl_new1(dig_mix, codec);
|
|
kctl = snd_ctl_new1(dig_mix, codec);
|
|
kctl->private_value = nid;
|
|
kctl->private_value = nid;
|
|
- err = snd_ctl_add(codec->bus->card, kctl);
|
|
|
|
|
|
+ err = snd_hda_ctl_add(codec, kctl);
|
|
if (err < 0)
|
|
if (err < 0)
|
|
return err;
|
|
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 */
|
|
/* resume the all commands from the cache */
|
|
void snd_hda_codec_resume_cache(struct hda_codec *codec)
|
|
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;
|
|
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;
|
|
u32 key = buffer->key;
|
|
if (!key)
|
|
if (!key)
|
|
continue;
|
|
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
|
|
#ifdef SND_HDA_NEEDS_RESUME
|
|
/*
|
|
/*
|
|
* call suspend and power-down; used both from PM and power-save
|
|
* 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,
|
|
hda_set_power_state(codec,
|
|
codec->afg ? codec->afg : codec->mfg,
|
|
codec->afg ? codec->afg : codec->mfg,
|
|
AC_PWRST_D0);
|
|
AC_PWRST_D0);
|
|
|
|
+ hda_exec_init_verbs(codec);
|
|
if (codec->patch_ops.resume)
|
|
if (codec->patch_ops.resume)
|
|
codec->patch_ops.resume(codec);
|
|
codec->patch_ops.resume(codec);
|
|
else {
|
|
else {
|
|
@@ -1918,23 +2050,31 @@ int __devinit snd_hda_build_controls(struct hda_bus *bus)
|
|
struct hda_codec *codec;
|
|
struct hda_codec *codec;
|
|
|
|
|
|
list_for_each_entry(codec, &bus->codec_list, list) {
|
|
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)
|
|
if (err < 0)
|
|
return err;
|
|
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;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -2235,8 +2375,8 @@ static int hda_pcm_default_cleanup(struct hda_pcm_stream *hinfo,
|
|
return 0;
|
|
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 */
|
|
/* query support PCM information from the given NID */
|
|
if (info->nid && (!info->rates || !info->formats)) {
|
|
if (info->nid && (!info->rates || !info->formats)) {
|
|
@@ -2262,6 +2402,28 @@ static int __devinit set_pcm_default_values(struct hda_codec *codec,
|
|
return 0;
|
|
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
|
|
* snd_hda_build_pcms - build PCM information
|
|
* @bus: the BUS
|
|
* @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.
|
|
* 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;
|
|
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) {
|
|
list_for_each_entry(codec, &bus->codec_list, list) {
|
|
- unsigned int pcm, s;
|
|
|
|
|
|
+ unsigned int pcm;
|
|
int err;
|
|
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 (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;
|
|
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)
|
|
if (err < 0)
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
@@ -2332,11 +2536,11 @@ int snd_hda_check_board_config(struct hda_codec *codec,
|
|
int num_configs, const char **models,
|
|
int num_configs, const char **models,
|
|
const struct snd_pci_quirk *tbl)
|
|
const struct snd_pci_quirk *tbl)
|
|
{
|
|
{
|
|
- if (codec->bus->modelname && models) {
|
|
|
|
|
|
+ if (codec->modelname && models) {
|
|
int i;
|
|
int i;
|
|
for (i = 0; i < num_configs; i++) {
|
|
for (i = 0; i < num_configs; i++) {
|
|
if (models[i] &&
|
|
if (models[i] &&
|
|
- !strcmp(codec->bus->modelname, models[i])) {
|
|
|
|
|
|
+ !strcmp(codec->modelname, models[i])) {
|
|
snd_printd(KERN_INFO "hda_codec: model '%s' is "
|
|
snd_printd(KERN_INFO "hda_codec: model '%s' is "
|
|
"selected\n", models[i]);
|
|
"selected\n", models[i]);
|
|
return 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);
|
|
kctl = snd_ctl_new1(knew, codec);
|
|
if (!kctl)
|
|
if (!kctl)
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
- err = snd_ctl_add(codec->bus->card, kctl);
|
|
|
|
|
|
+ err = snd_hda_ctl_add(codec, kctl);
|
|
if (err < 0) {
|
|
if (err < 0) {
|
|
if (!codec->addr)
|
|
if (!codec->addr)
|
|
return err;
|
|
return err;
|
|
@@ -2397,7 +2601,7 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
|
|
if (!kctl)
|
|
if (!kctl)
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
kctl->id.device = codec->addr;
|
|
kctl->id.device = codec->addr;
|
|
- err = snd_ctl_add(codec->bus->card, kctl);
|
|
|
|
|
|
+ err = snd_hda_ctl_add(codec, kctl);
|
|
if (err < 0)
|
|
if (err < 0)
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
@@ -3138,3 +3342,37 @@ int snd_hda_codecs_inuse(struct hda_bus *bus)
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
#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;
|
|
|
|
+}
|