mirror of https://gitee.com/openkylin/linux.git
Merge branch 'topic/hda-reconfig' into topic/hda-next
This commit is contained in:
commit
dd125b28c1
|
@ -344,7 +344,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 +393,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 +422,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 +461,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 +469,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 +496,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 +516,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 +589,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 +638,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 +691,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 +742,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 +799,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 +813,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 +965,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 +1120,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 +1188,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 +1652,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 +1696,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 +1798,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 +1853,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 +1941,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 +1978,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 +2004,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 +2329,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 +2356,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 +2404,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 +2490,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 +2547,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 +2555,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 +3296,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;
|
||||
}
|
||||
|
|
|
@ -519,6 +519,26 @@ enum {
|
|||
/* max. codec address */
|
||||
#define HDA_MAX_CODEC_ADDRESS 0x0f
|
||||
|
||||
/*
|
||||
* generic arrays
|
||||
*/
|
||||
struct snd_array {
|
||||
unsigned int used;
|
||||
unsigned int alloced;
|
||||
unsigned int elem_size;
|
||||
unsigned int alloc_align;
|
||||
void *list;
|
||||
};
|
||||
|
||||
void *snd_array_new(struct snd_array *array);
|
||||
void snd_array_free(struct snd_array *array);
|
||||
static inline void snd_array_init(struct snd_array *array, unsigned int size,
|
||||
unsigned int align)
|
||||
{
|
||||
array->elem_size = size;
|
||||
array->alloc_align = align;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -511,12 +511,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);
|
||||
|
|
|
@ -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_kctls(struct hda_codec *codec)
|
||||
{
|
||||
struct ad198x_spec *spec = codec->spec;
|
||||
|
||||
if (spec->kctls.list) {
|
||||
struct snd_kcontrol_new *kctl = spec->kctls.list;
|
||||
int i;
|
||||
for (i = 0; i < spec->kctls.used; i++)
|
||||
kfree(kctl[i].name);
|
||||
}
|
||||
snd_array_free(&spec->kctls);
|
||||
}
|
||||
|
||||
static void ad198x_free(struct hda_codec *codec)
|
||||
{
|
||||
struct ad198x_spec *spec = codec->spec;
|
||||
unsigned int i;
|
||||
|
||||
if (spec->kctl_alloc) {
|
||||
for (i = 0; i < spec->num_kctl_used; i++)
|
||||
kfree(spec->kctl_alloc[i].name);
|
||||
kfree(spec->kctl_alloc);
|
||||
}
|
||||
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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -5212,7 +5206,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)
|
||||
|
@ -5222,8 +5216,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;
|
||||
|
||||
|
@ -10296,8 +10290,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;
|
||||
|
@ -11427,8 +11421,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;
|
||||
|
@ -12199,8 +12193,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++)
|
||||
|
@ -13297,8 +13291,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;
|
||||
|
||||
|
@ -14408,8 +14402,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;
|
||||
|
@ -16235,8 +16229,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;
|
||||
|
|
|
@ -36,7 +36,6 @@
|
|||
#include "hda_patch.h"
|
||||
#include "hda_beep.h"
|
||||
|
||||
#define NUM_CONTROL_ALLOC 32
|
||||
#define STAC_PWR_EVENT 0x20
|
||||
#define STAC_HP_EVENT 0x30
|
||||
#define STAC_VREF_EVENT 0x40
|
||||
|
@ -222,8 +221,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;
|
||||
|
@ -1237,6 +1235,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;
|
||||
|
@ -1254,7 +1254,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;
|
||||
|
@ -1309,6 +1309,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
|
|||
return err;
|
||||
}
|
||||
|
||||
stac92xx_free_kctls(codec); /* no longer needed */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2596,28 +2597,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;
|
||||
}
|
||||
|
||||
|
@ -3438,8 +3427,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;
|
||||
|
@ -3540,8 +3529,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;
|
||||
|
@ -3708,20 +3697,26 @@ static int stac92xx_init(struct hda_codec *codec)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void stac92xx_free_kctls(struct hda_codec *codec)
|
||||
{
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
|
||||
if (spec->kctls.list) {
|
||||
struct snd_kcontrol_new *kctl = spec->kctls.list;
|
||||
int i;
|
||||
for (i = 0; i < spec->kctls.used; i++)
|
||||
kfree(kctl[i].name);
|
||||
}
|
||||
snd_array_free(&spec->kctls);
|
||||
}
|
||||
|
||||
static void stac92xx_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);
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
||||
|
|
Loading…
Reference in New Issue