diff options
Diffstat (limited to 'sound/pci/hda/hda_codec.c')
-rw-r--r-- | sound/pci/hda/hda_codec.c | 81 |
1 files changed, 73 insertions, 8 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index f98b47cd6cfb..76d3c4c049db 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -824,6 +824,9 @@ int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list, struct hda_pincfg *pin; unsigned int oldcfg; + if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) + return -EINVAL; + oldcfg = snd_hda_codec_get_pincfg(codec, nid); pin = look_up_pincfg(codec, list, nid); if (!pin) { @@ -899,6 +902,25 @@ static void restore_pincfgs(struct hda_codec *codec) } } +/** + * snd_hda_shutup_pins - Shut up all pins + * @codec: the HDA codec + * + * Clear all pin controls to shup up before suspend for avoiding click noise. + * The controls aren't cached so that they can be resumed properly. + */ +void snd_hda_shutup_pins(struct hda_codec *codec) +{ + int i; + for (i = 0; i < codec->init_pins.used; i++) { + struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); + /* use read here for syncing after issuing each verb */ + snd_hda_codec_read(codec, pin->nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0); + } +} +EXPORT_SYMBOL_HDA(snd_hda_shutup_pins); + static void init_hda_cache(struct hda_cache_rec *cache, unsigned int record_size); static void free_hda_cache(struct hda_cache_rec *cache); @@ -931,6 +953,7 @@ static void snd_hda_codec_free(struct hda_codec *codec) #endif list_del(&codec->list); snd_array_free(&codec->mixers); + snd_array_free(&codec->nids); codec->bus->caddr_tbl[codec->addr] = NULL; if (codec->patch_ops.free) codec->patch_ops.free(codec); @@ -985,7 +1008,8 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr mutex_init(&codec->control_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 hda_nid_item), 60); + snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32); + snd_array_init(&codec->nids, sizeof(struct hda_nid_item), 32); snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16); snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16); if (codec->bus->modelname) { @@ -1708,7 +1732,7 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl); /** - * snd_hda_ctl-add - Add a control element and assign to the codec + * snd_hda_ctl_add - Add a control element and assign to the codec * @codec: HD-audio codec * @nid: corresponding NID (optional) * @kctl: the control element to assign @@ -1723,19 +1747,25 @@ EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl); * * snd_hda_ctl_add() checks the control subdev id field whether * #HDA_SUBDEV_NID_FLAG bit is set. If set (and @nid is zero), the lower - * bits value is taken as the NID to assign. + * bits value is taken as the NID to assign. The #HDA_NID_ITEM_AMP bit + * specifies if kctl->private_value is a HDA amplifier value. */ int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, struct snd_kcontrol *kctl) { int err; + unsigned short flags = 0; struct hda_nid_item *item; - if (kctl->id.subdevice & HDA_SUBDEV_NID_FLAG) { + if (kctl->id.subdevice & HDA_SUBDEV_AMP_FLAG) { + flags |= HDA_NID_ITEM_AMP; if (nid == 0) - nid = kctl->id.subdevice & 0xffff; - kctl->id.subdevice = 0; + nid = get_amp_nid_(kctl->private_value); } + if ((kctl->id.subdevice & HDA_SUBDEV_NID_FLAG) != 0 && nid == 0) + nid = kctl->id.subdevice & 0xffff; + if (kctl->id.subdevice & (HDA_SUBDEV_NID_FLAG|HDA_SUBDEV_AMP_FLAG)) + kctl->id.subdevice = 0; err = snd_ctl_add(codec->bus->card, kctl); if (err < 0) return err; @@ -1744,11 +1774,41 @@ int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, return -ENOMEM; item->kctl = kctl; item->nid = nid; + item->flags = flags; return 0; } EXPORT_SYMBOL_HDA(snd_hda_ctl_add); /** + * snd_hda_add_nid - Assign a NID to a control element + * @codec: HD-audio codec + * @nid: corresponding NID (optional) + * @kctl: the control element to assign + * @index: index to kctl + * + * Add the given control element to an array inside the codec instance. + * This function is used when #snd_hda_ctl_add cannot be used for 1:1 + * NID:KCTL mapping - for example "Capture Source" selector. + */ +int snd_hda_add_nid(struct hda_codec *codec, struct snd_kcontrol *kctl, + unsigned int index, hda_nid_t nid) +{ + struct hda_nid_item *item; + + if (nid > 0) { + item = snd_array_new(&codec->nids); + if (!item) + return -ENOMEM; + item->kctl = kctl; + item->index = index; + item->nid = nid; + return 0; + } + return -EINVAL; +} +EXPORT_SYMBOL_HDA(snd_hda_add_nid); + +/** * snd_hda_ctls_clear - Clear all controls assigned to the given codec * @codec: HD-audio codec */ @@ -1759,6 +1819,7 @@ void snd_hda_ctls_clear(struct hda_codec *codec) for (i = 0; i < codec->mixers.used; i++) snd_ctl_remove(codec->bus->card, items[i].kctl); snd_array_free(&codec->mixers); + snd_array_free(&codec->nids); } /* pseudo device locking @@ -2706,7 +2767,8 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, power_state); /* partial workaround for "azx_get_response timeout" */ - if (power_state == AC_PWRST_D0) + if (power_state == AC_PWRST_D0 && + (codec->vendor_id & 0xffff0000) == 0x14f10000) msleep(10); nid = codec->start_nid; @@ -2740,7 +2802,6 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, if (power_state == AC_PWRST_D0) { unsigned long end_time; int state; - msleep(10); /* wait until the codec reachs to D0 */ end_time = jiffies + msecs_to_jiffies(500); do { @@ -3214,6 +3275,8 @@ const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = { /* * get the empty PCM device number to assign + * + * note the max device number is limited by HDA_MAX_PCMS, currently 10 */ static int get_empty_pcm_device(struct hda_bus *bus, int type) { @@ -3478,6 +3541,8 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew) for (; knew->name; knew++) { struct snd_kcontrol *kctl; + if (knew->iface == -1) /* skip this codec private value */ + continue; kctl = snd_ctl_new1(knew, codec); if (!kctl) return -ENOMEM; |