diff options
Diffstat (limited to 'sound/pci/hda/hda_codec.c')
-rw-r--r-- | sound/pci/hda/hda_codec.c | 214 |
1 files changed, 138 insertions, 76 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index d010de12335e..8353c77536ac 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -738,7 +738,7 @@ static int snd_hda_bus_dev_register(struct snd_device *device) * * Returns 0 if successful, or a negative error code. */ -int /*__devinit*/ snd_hda_bus_new(struct snd_card *card, +int snd_hda_bus_new(struct snd_card *card, const struct hda_bus_template *temp, struct hda_bus **busp) { @@ -908,7 +908,7 @@ static int get_codec_name(struct hda_codec *codec) /* * look for an AFG and MFG nodes */ -static void /*__devinit*/ setup_fg_nodes(struct hda_codec *codec) +static void setup_fg_nodes(struct hda_codec *codec) { int i, total_nodes, function_id; hda_nid_t nid; @@ -993,19 +993,6 @@ static struct hda_pincfg *look_up_pincfg(struct hda_codec *codec, return NULL; } -/* write a config value for the given NID */ -static void set_pincfg(struct hda_codec *codec, hda_nid_t nid, - unsigned int cfg) -{ - int i; - for (i = 0; i < 4; i++) { - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 + i, - cfg & 0xff); - cfg >>= 8; - } -} - /* set the current pin config value for the given NID. * the value is cached, and read via snd_hda_codec_get_pincfg() */ @@ -1013,12 +1000,10 @@ int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list, hda_nid_t nid, unsigned int cfg) { 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) { pin = snd_array_new(list); @@ -1027,13 +1012,6 @@ int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list, pin->nid = nid; } pin->cfg = cfg; - - /* change only when needed; e.g. if the pincfg is already present - * in user_pins[], don't write it - */ - cfg = snd_hda_codec_get_pincfg(codec, nid); - if (oldcfg != cfg) - set_pincfg(codec, nid, cfg); return 0; } @@ -1082,17 +1060,6 @@ unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid) } EXPORT_SYMBOL_HDA(snd_hda_codec_get_pincfg); -/* restore all current pin configs */ -static void restore_pincfgs(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); - set_pincfg(codec, pin->nid, - snd_hda_codec_get_pincfg(codec, pin->nid)); - } -} - /** * snd_hda_shutup_pins - Shut up all pins * @codec: the HDA codec @@ -1137,21 +1104,30 @@ static void restore_shutup_pins(struct hda_codec *codec) } #endif +static void hda_jackpoll_work(struct work_struct *work) +{ + struct hda_codec *codec = + container_of(work, struct hda_codec, jackpoll_work.work); + if (!codec->jackpoll_interval) + return; + + snd_hda_jack_set_dirty_all(codec); + snd_hda_jack_poll_all(codec); + queue_delayed_work(codec->bus->workq, &codec->jackpoll_work, + codec->jackpoll_interval); +} + static void init_hda_cache(struct hda_cache_rec *cache, unsigned int record_size); static void free_hda_cache(struct hda_cache_rec *cache); -/* restore the initial pin cfgs and release all pincfg lists */ -static void restore_init_pincfgs(struct hda_codec *codec) +/* release all pincfg lists */ +static void free_init_pincfgs(struct hda_codec *codec) { - /* first free driver_pins and user_pins, then call restore_pincfg - * so that only the values in init_pins are restored - */ snd_array_free(&codec->driver_pins); #ifdef CONFIG_SND_HDA_HWDEP snd_array_free(&codec->user_pins); #endif - restore_pincfgs(codec); snd_array_free(&codec->init_pins); } @@ -1192,8 +1168,9 @@ static void snd_hda_codec_free(struct hda_codec *codec) { if (!codec) return; + cancel_delayed_work_sync(&codec->jackpoll_work); snd_hda_jack_tbl_clear(codec); - restore_init_pincfgs(codec); + free_init_pincfgs(codec); #ifdef CONFIG_PM cancel_delayed_work(&codec->power_work); flush_workqueue(codec->bus->workq); @@ -1235,7 +1212,7 @@ static unsigned int hda_set_power_state(struct hda_codec *codec, * * Returns 0 if successful, or a negative error code. */ -int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, +int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, struct hda_codec **codecp) { @@ -1275,6 +1252,8 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8); snd_array_init(&codec->conn_lists, sizeof(hda_nid_t), 64); snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16); + snd_array_init(&codec->jacktbl, sizeof(struct hda_jack_tbl), 16); + INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work); #ifdef CONFIG_PM spin_lock_init(&codec->power_lock); @@ -1588,7 +1567,7 @@ static void hda_cleanup_all_streams(struct hda_codec *codec) #define INFO_AMP_VOL(ch) (1 << (1 + (ch))) /* initialize the hash table */ -static void /*__devinit*/ init_hda_cache(struct hda_cache_rec *cache, +static void init_hda_cache(struct hda_cache_rec *cache, unsigned int record_size) { memset(cache, 0, sizeof(*cache)); @@ -2153,12 +2132,12 @@ EXPORT_SYMBOL_HDA(snd_hda_set_vmaster_tlv); /* find a mixer control element with the given name */ static struct snd_kcontrol * -_snd_hda_find_mixer_ctl(struct hda_codec *codec, - const char *name, int idx) +find_mixer_ctl(struct hda_codec *codec, const char *name, int dev, int idx) { struct snd_ctl_elem_id id; memset(&id, 0, sizeof(id)); id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + id.device = dev; id.index = idx; if (snd_BUG_ON(strlen(name) >= sizeof(id.name))) return NULL; @@ -2176,15 +2155,16 @@ _snd_hda_find_mixer_ctl(struct hda_codec *codec, struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, const char *name) { - return _snd_hda_find_mixer_ctl(codec, name, 0); + return find_mixer_ctl(codec, name, 0, 0); } EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl); -static int find_empty_mixer_ctl_idx(struct hda_codec *codec, const char *name) +static int find_empty_mixer_ctl_idx(struct hda_codec *codec, const char *name, + int dev) { int idx; for (idx = 0; idx < 16; idx++) { /* 16 ctlrs should be large enough */ - if (!_snd_hda_find_mixer_ctl(codec, name, idx)) + if (!find_mixer_ctl(codec, name, dev, idx)) return idx; } return -EBUSY; @@ -2351,7 +2331,7 @@ int snd_hda_codec_reset(struct hda_codec *codec) return -EBUSY; /* OK, let it free */ - + cancel_delayed_work_sync(&codec->jackpoll_work); #ifdef CONFIG_PM cancel_delayed_work_sync(&codec->power_work); codec->power_on = 0; @@ -2380,7 +2360,6 @@ int snd_hda_codec_reset(struct hda_codec *codec) init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head)); /* free only driver_pins so that init_pins + user_pins are restored */ snd_array_free(&codec->driver_pins); - restore_pincfgs(codec); snd_array_free(&codec->cvt_setups); snd_array_free(&codec->spdif_out); codec->num_pcms = 0; @@ -3135,26 +3114,48 @@ static struct snd_kcontrol_new dig_mixes[] = { }; /** - * snd_hda_create_spdif_out_ctls - create Output SPDIF-related controls + * snd_hda_create_dig_out_ctls - create Output SPDIF-related controls * @codec: the HDA codec - * @nid: audio out widget NID - * - * Creates controls related with the SPDIF output. - * Called from each patch supporting the SPDIF out. + * @associated_nid: NID that new ctls associated with + * @cvt_nid: converter NID + * @type: HDA_PCM_TYPE_* + * Creates controls related with the digital output. + * Called from each patch supporting the digital out. * * Returns 0 if successful, or a negative error code. */ -int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, - hda_nid_t associated_nid, - hda_nid_t cvt_nid) +int snd_hda_create_dig_out_ctls(struct hda_codec *codec, + hda_nid_t associated_nid, + hda_nid_t cvt_nid, + int type) { int err; struct snd_kcontrol *kctl; struct snd_kcontrol_new *dig_mix; - int idx; + int idx, dev = 0; + const int spdif_pcm_dev = 1; struct hda_spdif_out *spdif; - idx = find_empty_mixer_ctl_idx(codec, "IEC958 Playback Switch"); + if (codec->primary_dig_out_type == HDA_PCM_TYPE_HDMI && + type == HDA_PCM_TYPE_SPDIF) { + dev = spdif_pcm_dev; + } else if (codec->primary_dig_out_type == HDA_PCM_TYPE_SPDIF && + type == HDA_PCM_TYPE_HDMI) { + for (idx = 0; idx < codec->spdif_out.used; idx++) { + spdif = snd_array_elem(&codec->spdif_out, idx); + for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) { + kctl = find_mixer_ctl(codec, dig_mix->name, 0, idx); + if (!kctl) + break; + kctl->id.device = spdif_pcm_dev; + } + } + codec->primary_dig_out_type = HDA_PCM_TYPE_HDMI; + } + if (!codec->primary_dig_out_type) + codec->primary_dig_out_type = type; + + idx = find_empty_mixer_ctl_idx(codec, "IEC958 Playback Switch", dev); if (idx < 0) { printk(KERN_ERR "hda_codec: too many IEC958 outputs\n"); return -EBUSY; @@ -3164,6 +3165,7 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, kctl = snd_ctl_new1(dig_mix, codec); if (!kctl) return -ENOMEM; + kctl->id.device = dev; kctl->id.index = idx; kctl->private_value = codec->spdif_out.used - 1; err = snd_hda_ctl_add(codec, associated_nid, kctl); @@ -3176,7 +3178,7 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, spdif->status = convert_to_spdif_status(spdif->ctls); return 0; } -EXPORT_SYMBOL_HDA(snd_hda_create_spdif_out_ctls); +EXPORT_SYMBOL_HDA(snd_hda_create_dig_out_ctls); /* get the hda_spdif_out entry from the given NID * call within spdif_mutex lock @@ -3351,7 +3353,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid) struct snd_kcontrol_new *dig_mix; int idx; - idx = find_empty_mixer_ctl_idx(codec, "IEC958 Capture Switch"); + idx = find_empty_mixer_ctl_idx(codec, "IEC958 Capture Switch", 0); if (idx < 0) { printk(KERN_ERR "hda_codec: too many IEC958 inputs\n"); return -EBUSY; @@ -3650,10 +3652,8 @@ static void hda_call_codec_resume(struct hda_codec *codec) */ hda_keep_power_on(codec); hda_set_power_state(codec, AC_PWRST_D0); - restore_pincfgs(codec); /* restore all current pin configs */ restore_shutup_pins(codec); hda_exec_init_verbs(codec); - snd_hda_jack_set_dirty_all(codec); if (codec->patch_ops.resume) codec->patch_ops.resume(codec); else { @@ -3662,7 +3662,13 @@ static void hda_call_codec_resume(struct hda_codec *codec) snd_hda_codec_resume_amp(codec); snd_hda_codec_resume_cache(codec); } - snd_hda_jack_report_sync(codec); + + if (codec->jackpoll_interval) + hda_jackpoll_work(&codec->jackpoll_work.work); + else { + snd_hda_jack_set_dirty_all(codec); + snd_hda_jack_report_sync(codec); + } codec->in_pm = 0; snd_hda_power_down(codec); /* flag down before returning */ @@ -3678,7 +3684,7 @@ static void hda_call_codec_resume(struct hda_codec *codec) * * Returns 0 if successful, otherwise a negative error code. */ -int /*__devinit*/ snd_hda_build_controls(struct hda_bus *bus) +int snd_hda_build_controls(struct hda_bus *bus) { struct hda_codec *codec; @@ -3712,13 +3718,14 @@ static int add_std_chmaps(struct hda_codec *codec) struct hda_pcm_stream *hinfo = &codec->pcm_info[i].stream[str]; struct snd_pcm_chmap *chmap; + const struct snd_pcm_chmap_elem *elem; if (codec->pcm_info[i].own_chmap) continue; if (!pcm || !hinfo->substreams) continue; - err = snd_pcm_add_chmap_ctls(pcm, str, - snd_pcm_std_chmaps, + elem = hinfo->chmap ? hinfo->chmap : snd_pcm_std_chmaps; + err = snd_pcm_add_chmap_ctls(pcm, str, elem, hinfo->channels_max, 0, &chmap); if (err < 0) @@ -3729,6 +3736,19 @@ static int add_std_chmaps(struct hda_codec *codec) return 0; } +/* default channel maps for 2.1 speakers; + * since HD-audio supports only stereo, odd number channels are omitted + */ +const struct snd_pcm_chmap_elem snd_pcm_2_1_chmaps[] = { + { .channels = 2, + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, + { .channels = 4, + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, + SNDRV_CHMAP_LFE, SNDRV_CHMAP_LFE } }, + { } +}; +EXPORT_SYMBOL_GPL(snd_pcm_2_1_chmaps); + int snd_hda_codec_build_controls(struct hda_codec *codec) { int err = 0; @@ -3746,7 +3766,10 @@ int snd_hda_codec_build_controls(struct hda_codec *codec) if (err < 0) return err; - snd_hda_jack_report_sync(codec); /* call at the last init point */ + if (codec->jackpoll_interval) + hda_jackpoll_work(&codec->jackpoll_work.work); + else + snd_hda_jack_report_sync(codec); /* call at the last init point */ return 0; } @@ -4458,7 +4481,7 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, addr = codec->addr; else if (!idx && !knew->index) { idx = find_empty_mixer_ctl_idx(codec, - knew->name); + knew->name, 0); if (idx <= 0) return err; } else @@ -4771,6 +4794,34 @@ EXPORT_SYMBOL_HDA(snd_hda_input_mux_put); /* + * process kcontrol info callback of a simple string enum array + * when @num_items is 0 or @texts is NULL, assume a boolean enum array + */ +int snd_hda_enum_helper_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo, + int num_items, const char * const *texts) +{ + static const char * const texts_default[] = { + "Disabled", "Enabled" + }; + + if (!texts || !num_items) { + num_items = 2; + texts = texts_default; + } + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = num_items; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + return 0; +} +EXPORT_SYMBOL_HDA(snd_hda_enum_helper_info); + +/* * Multi-channel / digital-out PCM helper functions */ @@ -4778,10 +4829,20 @@ EXPORT_SYMBOL_HDA(snd_hda_input_mux_put); static void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid, unsigned int stream_tag, unsigned int format) { - struct hda_spdif_out *spdif = snd_hda_spdif_out_of_nid(codec, nid); - - /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */ - if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE)) + struct hda_spdif_out *spdif; + unsigned int curr_fmt; + bool reset; + + spdif = snd_hda_spdif_out_of_nid(codec, nid); + curr_fmt = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_STREAM_FORMAT, 0); + reset = codec->spdif_status_reset && + (spdif->ctls & AC_DIG1_ENABLE) && + curr_fmt != format; + + /* turn off SPDIF if needed; otherwise the IEC958 bits won't be + updated */ + if (reset) set_dig_out_convert(codec, nid, spdif->ctls & ~AC_DIG1_ENABLE & 0xff, -1); @@ -4793,7 +4854,7 @@ static void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid, format); } /* turn on again (if needed) */ - if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE)) + if (reset) set_dig_out_convert(codec, nid, spdif->ctls & 0xff, -1); } @@ -5137,6 +5198,7 @@ int snd_hda_suspend(struct hda_bus *bus) struct hda_codec *codec; list_for_each_entry(codec, &bus->codec_list, list) { + cancel_delayed_work_sync(&codec->jackpoll_work); if (hda_codec_is_power_on(codec)) hda_call_codec_suspend(codec, false); } |