diff options
Diffstat (limited to 'sound/pci/hda')
-rw-r--r-- | sound/pci/hda/Kconfig | 10 | ||||
-rw-r--r-- | sound/pci/hda/hda_auto_parser.c | 56 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.c | 276 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.h | 82 | ||||
-rw-r--r-- | sound/pci/hda/hda_generic.c | 8 | ||||
-rw-r--r-- | sound/pci/hda/hda_hwdep.c | 43 | ||||
-rw-r--r-- | sound/pci/hda/hda_intel.c | 306 | ||||
-rw-r--r-- | sound/pci/hda/hda_jack.c | 37 | ||||
-rw-r--r-- | sound/pci/hda/hda_jack.h | 9 | ||||
-rw-r--r-- | sound/pci/hda/hda_local.h | 2 | ||||
-rw-r--r-- | sound/pci/hda/hda_proc.c | 9 | ||||
-rw-r--r-- | sound/pci/hda/hda_trace.h | 26 | ||||
-rw-r--r-- | sound/pci/hda/patch_analog.c | 90 | ||||
-rw-r--r-- | sound/pci/hda/patch_cirrus.c | 225 | ||||
-rw-r--r-- | sound/pci/hda/patch_conexant.c | 78 | ||||
-rw-r--r-- | sound/pci/hda/patch_hdmi.c | 407 | ||||
-rw-r--r-- | sound/pci/hda/patch_realtek.c | 119 | ||||
-rw-r--r-- | sound/pci/hda/patch_sigmatel.c | 115 | ||||
-rw-r--r-- | sound/pci/hda/patch_via.c | 37 |
19 files changed, 1400 insertions, 535 deletions
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 194d625..7105c3d 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -228,17 +228,9 @@ config SND_HDA_GENERIC Say Y here to enable the generic HD-audio codec parser in snd-hda-intel driver. -config SND_HDA_POWER_SAVE - bool "Aggressive power-saving on HD-audio" - depends on PM - help - Say Y here to enable more aggressive power-saving mode on - HD-audio driver. The power-saving timeout can be configured - via power_save option or over sysfs on-the-fly. - config SND_HDA_POWER_SAVE_DEFAULT int "Default time-out for HD-audio power-save mode" - depends on SND_HDA_POWER_SAVE + depends on PM default 0 help The default time-out value in seconds for HD-audio automatic diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 4f7d2df..4ec6dc8 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -141,7 +141,6 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, memset(sequences_hp, 0, sizeof(sequences_hp)); assoc_line_out = 0; - codec->ignore_misc_bit = true; end_nid = codec->start_nid + codec->num_nodes; for (nid = codec->start_nid; nid < end_nid; nid++) { unsigned int wid_caps = get_wcaps(codec, nid); @@ -157,9 +156,6 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, continue; def_conf = snd_hda_codec_get_pincfg(codec, nid); - if (!(get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) & - AC_DEFCFG_MISC_NO_PRESENCE)) - codec->ignore_misc_bit = false; conn = get_defcfg_connect(def_conf); if (conn == AC_JACK_PORT_NONE) continue; @@ -502,6 +498,38 @@ static const char *check_output_sfx(hda_nid_t nid, const hda_nid_t *pins, return channel_sfx[i]; } +static const char *check_output_pfx(struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); + int attr = snd_hda_get_input_pin_attr(def_conf); + + /* check the location */ + switch (attr) { + case INPUT_PIN_ATTR_DOCK: + return "Dock "; + case INPUT_PIN_ATTR_FRONT: + return "Front "; + } + return ""; +} + +static int get_hp_label_index(struct hda_codec *codec, hda_nid_t nid, + const hda_nid_t *pins, int num_pins) +{ + int i, j, idx = 0; + + const char *pfx = check_output_pfx(codec, nid); + + i = find_idx_in_nid_list(nid, pins, num_pins); + if (i < 0) + return -1; + for (j = 0; j < i; j++) + if (pfx == check_output_pfx(codec, pins[j])) + idx++; + + return idx; +} + static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid, const struct auto_pin_cfg *cfg, const char *name, char *label, int maxlen, @@ -509,20 +537,13 @@ static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid, { unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); int attr = snd_hda_get_input_pin_attr(def_conf); - const char *pfx = "", *sfx = ""; + const char *pfx, *sfx = ""; /* handle as a speaker if it's a fixed line-out */ if (!strcmp(name, "Line Out") && attr == INPUT_PIN_ATTR_INT) name = "Speaker"; - /* check the location */ - switch (attr) { - case INPUT_PIN_ATTR_DOCK: - pfx = "Dock "; - break; - case INPUT_PIN_ATTR_FRONT: - pfx = "Front "; - break; - } + pfx = check_output_pfx(codec, nid); + if (cfg) { /* try to give a unique suffix if needed */ sfx = check_output_sfx(nid, cfg->line_out_pins, cfg->line_outs, @@ -532,8 +553,8 @@ static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid, indexp); if (!sfx) { /* don't add channel suffix for Headphone controls */ - int idx = find_idx_in_nid_list(nid, cfg->hp_pins, - cfg->hp_outs); + int idx = get_hp_label_index(codec, nid, cfg->hp_pins, + cfg->hp_outs); if (idx >= 0) *indexp = idx; sfx = ""; @@ -739,7 +760,8 @@ void snd_hda_pick_fixup(struct hda_codec *codec, for (q = quirk; q->subvendor; q++) { unsigned int vendorid = q->subdevice | (q->subvendor << 16); - if (vendorid == codec->subsystem_id) { + unsigned int mask = 0xffff0000 | q->subdevice_mask; + if ((codec->subsystem_id & mask) == (vendorid & mask)) { id = q->value; #ifdef CONFIG_SND_DEBUG_VERBOSE name = q->name; diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 1c65cc5..c0ab72c 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -94,13 +94,19 @@ int snd_hda_delete_codec_preset(struct hda_codec_preset_list *preset) } EXPORT_SYMBOL_HDA(snd_hda_delete_codec_preset); -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM static void hda_power_work(struct work_struct *work); static void hda_keep_power_on(struct hda_codec *codec); #define hda_codec_is_power_on(codec) ((codec)->power_on) +static inline void hda_call_pm_notify(struct hda_bus *bus, bool power_up) +{ + if (bus->ops.pm_notify) + bus->ops.pm_notify(bus, power_up); +} #else static inline void hda_keep_power_on(struct hda_codec *codec) {} #define hda_codec_is_power_on(codec) 1 +#define hda_call_pm_notify(bus, state) {} #endif /** @@ -808,7 +814,7 @@ find_codec_preset(struct hda_codec *codec) { struct hda_codec_preset_list *tbl; const struct hda_codec_preset *preset; - int mod_requested = 0; + unsigned int mod_requested = 0; if (is_generic_config(codec)) return NULL; /* use the generic parser */ @@ -1186,7 +1192,7 @@ static void snd_hda_codec_free(struct hda_codec *codec) return; snd_hda_jack_tbl_clear(codec); restore_init_pincfgs(codec); -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM cancel_delayed_work(&codec->power_work); flush_workqueue(codec->bus->workq); #endif @@ -1199,6 +1205,10 @@ static void snd_hda_codec_free(struct hda_codec *codec) codec->bus->caddr_tbl[codec->addr] = NULL; if (codec->patch_ops.free) codec->patch_ops.free(codec); +#ifdef CONFIG_PM + if (!codec->pm_down_notified) /* cancel leftover refcounts */ + hda_call_pm_notify(codec->bus, false); +#endif module_put(codec->owner); free_hda_cache(&codec->amp_cache); free_hda_cache(&codec->cmd_cache); @@ -1212,7 +1222,7 @@ static void snd_hda_codec_free(struct hda_codec *codec) static bool snd_hda_codec_get_supported_ps(struct hda_codec *codec, hda_nid_t fg, unsigned int power_state); -static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, +static unsigned int hda_set_power_state(struct hda_codec *codec, unsigned int power_state); /** @@ -1229,6 +1239,7 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, { struct hda_codec *codec; char component[31]; + hda_nid_t fg; int err; if (snd_BUG_ON(!bus)) @@ -1263,7 +1274,7 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, snd_array_init(&codec->conn_lists, sizeof(hda_nid_t), 64); snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16); -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM spin_lock_init(&codec->power_lock); INIT_DELAYED_WORK(&codec->power_work, hda_power_work); /* snd_hda_codec_new() marks the codec as power-up, and leave it as is. @@ -1271,6 +1282,7 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, * phase. */ hda_keep_power_on(codec); + hda_call_pm_notify(bus, true); #endif if (codec->bus->modelname) { @@ -1304,7 +1316,8 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, goto error; } - err = read_widget_caps(codec, codec->afg ? codec->afg : codec->mfg); + fg = codec->afg ? codec->afg : codec->mfg; + err = read_widget_caps(codec, fg); if (err < 0) { snd_printk(KERN_ERR "hda_codec: cannot malloc\n"); goto error; @@ -1314,20 +1327,22 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, goto error; if (!codec->subsystem_id) { - hda_nid_t nid = codec->afg ? codec->afg : codec->mfg; codec->subsystem_id = - snd_hda_codec_read(codec, nid, 0, + snd_hda_codec_read(codec, fg, 0, AC_VERB_GET_SUBSYSTEM_ID, 0); } - codec->epss = snd_hda_codec_get_supported_ps(codec, - codec->afg ? codec->afg : codec->mfg, +#ifdef CONFIG_PM + codec->d3_stop_clk = snd_hda_codec_get_supported_ps(codec, fg, + AC_PWRST_CLKSTOP); + if (!codec->d3_stop_clk) + bus->power_keep_link_on = 1; +#endif + codec->epss = snd_hda_codec_get_supported_ps(codec, fg, AC_PWRST_EPSS); /* power-up all before initialization */ - hda_set_power_state(codec, - codec->afg ? codec->afg : codec->mfg, - AC_PWRST_D0); + hda_set_power_state(codec, AC_PWRST_D0); snd_hda_codec_proc_new(codec); @@ -2335,7 +2350,7 @@ int snd_hda_codec_reset(struct hda_codec *codec) /* OK, let it free */ -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM cancel_delayed_work_sync(&codec->power_work); codec->power_on = 0; codec->power_transition = 0; @@ -3500,20 +3515,6 @@ void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, power_state); } - - if (power_state == AC_PWRST_D0) { - unsigned long end_time; - int state; - /* wait until the codec reachs to D0 */ - end_time = jiffies + msecs_to_jiffies(500); - do { - state = snd_hda_codec_read(codec, fg, 0, - AC_VERB_GET_POWER_STATE, 0); - if (state == power_state) - break; - msleep(1); - } while (time_after_eq(end_time, jiffies)); - } } EXPORT_SYMBOL_HDA(snd_hda_codec_set_power_to_all); @@ -3534,18 +3535,40 @@ static bool snd_hda_codec_get_supported_ps(struct hda_codec *codec, hda_nid_t fg } /* - * set power state of the codec + * wait until the state is reached, returns the current state */ -static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, - unsigned int power_state) +static unsigned int hda_sync_power_state(struct hda_codec *codec, + hda_nid_t fg, + unsigned int power_state) { - int count; - unsigned int state; + unsigned long end_time = jiffies + msecs_to_jiffies(500); + unsigned int state, actual_state; - if (codec->patch_ops.set_power_state) { - codec->patch_ops.set_power_state(codec, fg, power_state); - return; + for (;;) { + state = snd_hda_codec_read(codec, fg, 0, + AC_VERB_GET_POWER_STATE, 0); + if (state & AC_PWRST_ERROR) + break; + actual_state = (state >> 4) & 0x0f; + if (actual_state == power_state) + break; + if (time_after_eq(jiffies, end_time)) + break; + /* wait until the codec reachs to the target state */ + msleep(1); } + return state; +} + +/* + * set power state of the codec, and return the power state + */ +static unsigned int hda_set_power_state(struct hda_codec *codec, + unsigned int power_state) +{ + hda_nid_t fg = codec->afg ? codec->afg : codec->mfg; + int count; + unsigned int state; /* this delay seems necessary to avoid click noise at power-down */ if (power_state == AC_PWRST_D3) { @@ -3555,14 +3578,22 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, /* repeat power states setting at most 10 times*/ for (count = 0; count < 10; count++) { - snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, - power_state); - snd_hda_codec_set_power_to_all(codec, fg, power_state, true); - state = snd_hda_codec_read(codec, fg, 0, - AC_VERB_GET_POWER_STATE, 0); + if (codec->patch_ops.set_power_state) + codec->patch_ops.set_power_state(codec, fg, + power_state); + else { + snd_hda_codec_read(codec, fg, 0, + AC_VERB_SET_POWER_STATE, + power_state); + snd_hda_codec_set_power_to_all(codec, fg, power_state, + true); + } + state = hda_sync_power_state(codec, fg, power_state); if (!(state & AC_PWRST_ERROR)) break; } + + return state; } #ifdef CONFIG_SND_HDA_HWDEP @@ -3579,17 +3610,19 @@ static inline void hda_exec_init_verbs(struct hda_codec *codec) {} #ifdef CONFIG_PM /* * call suspend and power-down; used both from PM and power-save + * this function returns the power state in the end */ -static void hda_call_codec_suspend(struct hda_codec *codec) +static unsigned int hda_call_codec_suspend(struct hda_codec *codec, bool in_wq) { + unsigned int state; + if (codec->patch_ops.suspend) codec->patch_ops.suspend(codec); hda_cleanup_all_streams(codec); - hda_set_power_state(codec, - codec->afg ? codec->afg : codec->mfg, - AC_PWRST_D3); -#ifdef CONFIG_SND_HDA_POWER_SAVE - cancel_delayed_work(&codec->power_work); + state = hda_set_power_state(codec, AC_PWRST_D3); + /* Cancel delayed work if we aren't currently running from it. */ + if (!in_wq) + cancel_delayed_work_sync(&codec->power_work); spin_lock(&codec->power_lock); snd_hda_update_power_acct(codec); trace_hda_power_down(codec); @@ -3597,7 +3630,7 @@ static void hda_call_codec_suspend(struct hda_codec *codec) codec->power_transition = 0; codec->power_jiffies = jiffies; spin_unlock(&codec->power_lock); -#endif + return state; } /* @@ -3609,9 +3642,7 @@ static void hda_call_codec_resume(struct hda_codec *codec) * in the resume / power-save sequence */ hda_keep_power_on(codec); - hda_set_power_state(codec, - codec->afg ? codec->afg : codec->mfg, - AC_PWRST_D0); + 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); @@ -3624,6 +3655,7 @@ 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); snd_hda_power_down(codec); /* flag down before returning */ } #endif /* CONFIG_PM */ @@ -3658,6 +3690,36 @@ int /*__devinit*/ snd_hda_build_controls(struct hda_bus *bus) } EXPORT_SYMBOL_HDA(snd_hda_build_controls); +/* + * add standard channel maps if not specified + */ +static int add_std_chmaps(struct hda_codec *codec) +{ + int i, str, err; + + for (i = 0; i < codec->num_pcms; i++) { + for (str = 0; str < 2; str++) { + struct snd_pcm *pcm = codec->pcm_info[i].pcm; + struct hda_pcm_stream *hinfo = + &codec->pcm_info[i].stream[str]; + struct snd_pcm_chmap *chmap; + + 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, + hinfo->channels_max, + 0, &chmap); + if (err < 0) + return err; + chmap->channel_mask = SND_PCM_CHMAP_MASK_2468; + } + } + return 0; +} + int snd_hda_codec_build_controls(struct hda_codec *codec) { int err = 0; @@ -3669,6 +3731,13 @@ int snd_hda_codec_build_controls(struct hda_codec *codec) err = codec->patch_ops.build_controls(codec); if (err < 0) return err; + + /* we create chmaps here instead of build_pcms */ + err = add_std_chmaps(codec); + if (err < 0) + return err; + + snd_hda_jack_report_sync(codec); /* call at the last init point */ return 0; } @@ -4211,7 +4280,7 @@ int snd_hda_codec_build_pcms(struct hda_codec *codec) * * This function returns 0 if successful, or a negative error code. */ -int __devinit snd_hda_build_pcms(struct hda_bus *bus) +int snd_hda_build_pcms(struct hda_bus *bus) { struct hda_codec *codec; @@ -4391,12 +4460,13 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_add_new_ctls); -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM static void hda_power_work(struct work_struct *work) { struct hda_codec *codec = container_of(work, struct hda_codec, power_work.work); struct hda_bus *bus = codec->bus; + unsigned int state; spin_lock(&codec->power_lock); if (codec->power_transition > 0) { /* during power-up sequence? */ @@ -4410,9 +4480,12 @@ static void hda_power_work(struct work_struct *work) } spin_unlock(&codec->power_lock); - hda_call_codec_suspend(codec); - if (bus->ops.pm_notify) - bus->ops.pm_notify(bus); + state = hda_call_codec_suspend(codec, true); + codec->pm_down_notified = 0; + if (!bus->power_keep_link_on && (state & AC_PWRST_CLK_STOP_OK)) { + codec->pm_down_notified = 1; + hda_call_pm_notify(bus, false); + } } static void hda_keep_power_on(struct hda_codec *codec) @@ -4438,19 +4511,16 @@ void snd_hda_update_power_acct(struct hda_codec *codec) /* Transition to powered up, if wait_power_down then wait for a pending * transition to D3 to complete. A pending D3 transition is indicated * with power_transition == -1. */ +/* call this with codec->power_lock held! */ static void __snd_hda_power_up(struct hda_codec *codec, bool wait_power_down) { struct hda_bus *bus = codec->bus; - spin_lock(&codec->power_lock); - codec->power_count++; /* Return if power_on or transitioning to power_on, unless currently * powering down. */ if ((codec->power_on || codec->power_transition > 0) && - !(wait_power_down && codec->power_transition < 0)) { - spin_unlock(&codec->power_lock); + !(wait_power_down && codec->power_transition < 0)) return; - } spin_unlock(&codec->power_lock); cancel_delayed_work_sync(&codec->power_work); @@ -4462,9 +4532,9 @@ static void __snd_hda_power_up(struct hda_codec *codec, bool wait_power_down) if (codec->power_on) { if (codec->power_transition < 0) codec->power_transition = 0; - spin_unlock(&codec->power_lock); return; } + trace_hda_power_up(codec); snd_hda_update_power_acct(codec); codec->power_on = 1; @@ -4472,71 +4542,54 @@ static void __snd_hda_power_up(struct hda_codec *codec, bool wait_power_down) codec->power_transition = 1; /* avoid reentrance */ spin_unlock(&codec->power_lock); - if (bus->ops.pm_notify) - bus->ops.pm_notify(bus); + if (codec->pm_down_notified) { + codec->pm_down_notified = 0; + hda_call_pm_notify(bus, true); + } + hda_call_codec_resume(codec); spin_lock(&codec->power_lock); codec->power_transition = 0; - spin_unlock(&codec->power_lock); -} - -/** - * snd_hda_power_up - Power-up the codec - * @codec: HD-audio codec - * - * Increment the power-up counter and power up the hardware really when - * not turned on yet. - */ -void snd_hda_power_up(struct hda_codec *codec) -{ - __snd_hda_power_up(codec, false); } -EXPORT_SYMBOL_HDA(snd_hda_power_up); - -/** - * snd_hda_power_up_d3wait - Power-up the codec after waiting for any pending - * D3 transition to complete. This differs from snd_hda_power_up() when - * power_transition == -1. snd_hda_power_up sees this case as a nop, - * snd_hda_power_up_d3wait waits for the D3 transition to complete then powers - * back up. - * @codec: HD-audio codec - * - * Cancel any power down operation hapenning on the work queue, then power up. - */ -void snd_hda_power_up_d3wait(struct hda_codec *codec) -{ - /* This will cancel and wait for pending power_work to complete. */ - __snd_hda_power_up(codec, true); -} -EXPORT_SYMBOL_HDA(snd_hda_power_up_d3wait); #define power_save(codec) \ ((codec)->bus->power_save ? *(codec)->bus->power_save : 0) -/** - * snd_hda_power_down - Power-down the codec - * @codec: HD-audio codec - * - * Decrement the power-up counter and schedules the power-off work if - * the counter rearches to zero. - */ -void snd_hda_power_down(struct hda_codec *codec) +/* Transition to powered down */ +static void __snd_hda_power_down(struct hda_codec *codec) { - spin_lock(&codec->power_lock); - --codec->power_count; - if (!codec->power_on || codec->power_count || codec->power_transition) { - spin_unlock(&codec->power_lock); + if (!codec->power_on || codec->power_count || codec->power_transition) return; - } + if (power_save(codec)) { codec->power_transition = -1; /* avoid reentrance */ queue_delayed_work(codec->bus->workq, &codec->power_work, msecs_to_jiffies(power_save(codec) * 1000)); } +} + +/** + * snd_hda_power_save - Power-up/down/sync the codec + * @codec: HD-audio codec + * @delta: the counter delta to change + * + * Change the power-up counter via @delta, and power up or down the hardware + * appropriately. For the power-down, queue to the delayed action. + * Passing zero to @delta means to synchronize the power state. + */ +void snd_hda_power_save(struct hda_codec *codec, int delta, bool d3wait) +{ + spin_lock(&codec->power_lock); + codec->power_count += delta; + trace_hda_power_count(codec); + if (delta > 0) + __snd_hda_power_up(codec, d3wait); + else + __snd_hda_power_down(codec); spin_unlock(&codec->power_lock); } -EXPORT_SYMBOL_HDA(snd_hda_power_down); +EXPORT_SYMBOL_HDA(snd_hda_power_save); /** * snd_hda_check_amp_list_power - Check the amp list and update the power @@ -5076,7 +5129,7 @@ int snd_hda_suspend(struct hda_bus *bus) list_for_each_entry(codec, &bus->codec_list, list) { if (hda_codec_is_power_on(codec)) - hda_call_codec_suspend(codec); + hda_call_codec_suspend(codec, false); } return 0; } @@ -5087,9 +5140,6 @@ EXPORT_SYMBOL_HDA(snd_hda_suspend); * @bus: the HDA bus * * Returns 0 if successful. - * - * This function is defined only when POWER_SAVE isn't set. - * In the power-save mode, the codec is resumed dynamically. */ int snd_hda_resume(struct hda_bus *bus) { diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index e5a7e19..507fe8a 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -386,6 +386,10 @@ enum { /* DIGITAL2 bits */ #define AC_DIG2_CC (0x7f<<0) +/* DIGITAL3 bits */ +#define AC_DIG3_ICT (0xf<<0) +#define AC_DIG3_KAE (1<<7) + /* Pin widget control - 8bit */ #define AC_PINCTL_EPT (0x3<<0) #define AC_PINCTL_EPT_NATIVE 0 @@ -610,9 +614,9 @@ struct hda_bus_ops { struct hda_pcm *pcm); /* reset bus for retry verb */ void (*bus_reset)(struct hda_bus *bus); -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM /* notify power-up/down from codec to controller */ - void (*pm_notify)(struct hda_bus *bus); + void (*pm_notify)(struct hda_bus *bus, bool power_up); #endif }; @@ -708,8 +712,6 @@ struct hda_codec_ops { #ifdef CONFIG_PM int (*suspend)(struct hda_codec *codec); int (*resume)(struct hda_codec *codec); -#endif -#ifdef CONFIG_SND_HDA_POWER_SAVE int (*check_power_status)(struct hda_codec *codec, hda_nid_t nid); #endif void (*reboot_notify)(struct hda_codec *codec); @@ -774,6 +776,7 @@ struct hda_pcm { unsigned int pcm_type; /* HDA_PCM_TYPE_XXX */ int device; /* device number to assign */ struct snd_pcm *pcm; /* assigned PCM instance */ + bool own_chmap; /* codec driver provides own channel maps */ }; /* codec information */ @@ -859,12 +862,13 @@ struct hda_codec { unsigned int no_sticky_stream:1; /* no sticky-PCM stream assignment */ unsigned int pins_shutup:1; /* pins are shut up */ unsigned int no_trigger_sense:1; /* don't trigger at pin-sensing */ - unsigned int ignore_misc_bit:1; /* ignore MISC_NO_PRESENCE bit */ unsigned int no_jack_detect:1; /* Machine has no jack-detection */ unsigned int pcm_format_first:1; /* PCM format must be set first */ unsigned int epss:1; /* supporting EPSS? */ -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM unsigned int power_on :1; /* current (global) power-state */ + unsigned int d3_stop_clk:1; /* support D3 operation without BCLK */ + unsigned int pm_down_notified:1; /* PM notified to controller */ int power_transition; /* power-state in transition */ int power_count; /* current (global) power refcount */ struct delayed_work power_work; /* delayed task for powerdown */ @@ -1042,7 +1046,7 @@ int snd_hda_resume(struct hda_bus *bus); static inline int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid) { -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM if (codec->patch_ops.check_power_status) return codec->patch_ops.check_power_status(codec, nid); #endif @@ -1059,22 +1063,70 @@ const char *snd_hda_get_jack_location(u32 cfg); /* * power saving */ -#ifdef CONFIG_SND_HDA_POWER_SAVE -void snd_hda_power_up(struct hda_codec *codec); -void snd_hda_power_up_d3wait(struct hda_codec *codec); -void snd_hda_power_down(struct hda_codec *codec); +#ifdef CONFIG_PM +void snd_hda_power_save(struct hda_codec *codec, int delta, bool d3wait); void snd_hda_update_power_acct(struct hda_codec *codec); #else -static inline void snd_hda_power_up(struct hda_codec *codec) {} -static inline void snd_hda_power_up_d3wait(struct hda_codec *codec) {} -static inline void snd_hda_power_down(struct hda_codec *codec) {} +static inline void snd_hda_power_save(struct hda_codec *codec, int delta, + bool d3wait) {} #endif +/** + * snd_hda_power_up - Power-up the codec + * @codec: HD-audio codec + * + * Increment the power-up counter and power up the hardware really when + * not turned on yet. + */ +static inline void snd_hda_power_up(struct hda_codec *codec) +{ + snd_hda_power_save(codec, 1, false); +} + +/** + * snd_hda_power_up_d3wait - Power-up the codec after waiting for any pending + * D3 transition to complete. This differs from snd_hda_power_up() when + * power_transition == -1. snd_hda_power_up sees this case as a nop, + * snd_hda_power_up_d3wait waits for the D3 transition to complete then powers + * back up. + * @codec: HD-audio codec + * + * Cancel any power down operation hapenning on the work queue, then power up. + */ +static inline void snd_hda_power_up_d3wait(struct hda_codec *codec) +{ + snd_hda_power_save(codec, 1, true); +} + +/** + * snd_hda_power_down - Power-down the codec + * @codec: HD-audio codec + * + * Decrement the power-up counter and schedules the power-off work if + * the counter rearches to zero. + */ +static inline void snd_hda_power_down(struct hda_codec *codec) +{ + snd_hda_power_save(codec, -1, false); +} + +/** + * snd_hda_power_sync - Synchronize the power-save status + * @codec: HD-audio codec + * + * Synchronize the actual power state with the power account; + * called when power_save parameter is changed + */ +static inline void snd_hda_power_sync(struct hda_codec *codec) +{ + snd_hda_power_save(codec, 0, false); +} + #ifdef CONFIG_SND_HDA_PATCH_LOADER /* * patch firmware */ -int snd_hda_load_patch(struct hda_bus *bus, const char *patch); +int snd_hda_load_patch(struct hda_bus *bus, size_t size, const void *buf); #endif /* diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 431bf86..b81d3d0 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -70,7 +70,7 @@ struct hda_gspec { struct list_head nid_list; /* list of widgets */ -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM #define MAX_LOOPBACK_AMPS 7 struct hda_loopback_check loopback; int num_loopbacks; @@ -654,7 +654,7 @@ static int parse_input(struct hda_codec *codec) return 0; } -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM static void add_input_loopback(struct hda_codec *codec, hda_nid_t nid, int dir, int idx) { @@ -1028,7 +1028,7 @@ static int build_generic_pcms(struct hda_codec *codec) return 0; } -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM static int generic_check_power_status(struct hda_codec *codec, hda_nid_t nid) { struct hda_gspec *spec = codec->spec; @@ -1043,7 +1043,7 @@ static struct hda_codec_ops generic_patch_ops = { .build_controls = build_generic_controls, .build_pcms = build_generic_pcms, .free = snd_hda_generic_free, -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM .check_power_status = generic_check_power_status, #endif }; diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c index 6b2efb8..1af86d4 100644 --- a/sound/pci/hda/hda_hwdep.c +++ b/sound/pci/hda/hda_hwdep.c @@ -25,7 +25,6 @@ #include <linux/mutex.h> #include <linux/ctype.h> #include <linux/string.h> -#include <linux/firmware.h> #include <linux/export.h> #include <sound/core.h> #include "hda_codec.h" @@ -156,7 +155,7 @@ int /*__devinit*/ snd_hda_create_hwdep(struct hda_codec *codec) return 0; } -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM static ssize_t power_on_acct_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -192,7 +191,7 @@ int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec) hwdep->device, &power_attrs[i]); return 0; } -#endif /* CONFIG_SND_HDA_POWER_SAVE */ +#endif /* CONFIG_PM */ #ifdef CONFIG_SND_HDA_RECONFIG @@ -747,18 +746,21 @@ static int parse_line_mode(char *buf, struct hda_bus *bus) * * the spaces at the beginning and the end of the line are stripped */ -static int get_line_from_fw(char *buf, int size, struct firmware *fw) +static int get_line_from_fw(char *buf, int size, size_t *fw_size_p, + const void **fw_data_p) { int len; - const char *p = fw->data; - while (isspace(*p) && fw->size) { + size_t fw_size = *fw_size_p; + const char *p = *fw_data_p; + + while (isspace(*p) && fw_size) { p++; - fw->size--; + fw_size--; } - if (!fw->size) + if (!fw_size) return 0; - for (len = 0; len < fw->size; len++) { + for (len = 0; len < fw_size; len++) { if (!*p) break; if (*p == '\n') { @@ -770,8 +772,8 @@ static int get_line_from_fw(char *buf, int size, struct firmware *fw) *buf++ = *p++; } *buf = 0; - fw->size -= len; - fw->data = p; + *fw_size_p = fw_size - len; + *fw_data_p = p; remove_trail_spaces(buf); return 1; } @@ -779,29 +781,15 @@ static int get_line_from_fw(char *buf, int size, struct firmware *fw) /* * load a "patch" firmware file and parse it */ -int snd_hda_load_patch(struct hda_bus *bus, const char *patch) +int snd_hda_load_patch(struct hda_bus *bus, size_t fw_size, const void *fw_buf) { - int err; - const struct firmware *fw; - struct firmware tmp; char buf[128]; struct hda_codec *codec; int line_mode; - struct device *dev = bus->card->dev; - - if (snd_BUG_ON(!dev)) - return -ENODEV; - err = request_firmware(&fw, patch, dev); - if (err < 0) { - printk(KERN_ERR "hda-codec: Cannot load the patch '%s'\n", - patch); - return err; - } - tmp = *fw; line_mode = LINE_MODE_NONE; codec = NULL; - while (get_line_from_fw(buf, sizeof(buf) - 1, &tmp)) { + while (get_line_from_fw(buf, sizeof(buf) - 1, &fw_size, &fw_buf)) { if (!*buf || *buf == '#' || *buf == '\n') continue; if (*buf == '[') @@ -810,7 +798,6 @@ int snd_hda_load_patch(struct hda_bus *bus, const char *patch) (codec || !patch_items[line_mode].need_codec)) patch_items[line_mode].parser(buf, bus, &codec); } - release_firmware(fw); return 0; } EXPORT_SYMBOL_HDA(snd_hda_load_patch); diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index c4763c5..f09ff6c 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -46,6 +46,7 @@ #include <linux/mutex.h> #include <linux/reboot.h> #include <linux/io.h> +#include <linux/pm_runtime.h> #ifdef CONFIG_X86 /* for snoop control */ #include <asm/pgtable.h> @@ -55,6 +56,7 @@ #include <sound/initval.h> #include <linux/vgaarb.h> #include <linux/vga_switcheroo.h> +#include <linux/firmware.h> #include "hda_codec.h" @@ -62,7 +64,7 @@ static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; static char *model[SNDRV_CARDS]; -static int position_fix[SNDRV_CARDS]; +static int position_fix[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1}; static int bdl_pos_adj[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1}; static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1}; static int probe_only[SNDRV_CARDS]; @@ -86,7 +88,7 @@ module_param_array(model, charp, NULL, 0444); MODULE_PARM_DESC(model, "Use the given board model."); module_param_array(position_fix, int, NULL, 0444); MODULE_PARM_DESC(position_fix, "DMA pointer read method." - "(0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO, 4 = COMBO)."); + "(-1 = system default, 0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO, 4 = COMBO)."); module_param_array(bdl_pos_adj, int, NULL, 0644); MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset."); module_param_array(probe_mask, int, NULL, 0444); @@ -108,9 +110,16 @@ MODULE_PARM_DESC(beep_mode, "Select HDA Beep registration mode " "(0=off, 1=on) (default=1)."); #endif -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM +static int param_set_xint(const char *val, const struct kernel_param *kp); +static struct kernel_param_ops param_ops_xint = { + .set = param_set_xint, + .get = param_get_int, +}; +#define param_check_xint param_check_int + static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT; -module_param(power_save, int, 0644); +module_param(power_save, xint, 0644); MODULE_PARM_DESC(power_save, "Automatic power-saving timeout " "(in second, 0 = disable)."); @@ -121,7 +130,7 @@ MODULE_PARM_DESC(power_save, "Automatic power-saving timeout " static bool power_save_controller = 1; module_param(power_save_controller, bool, 0644); MODULE_PARM_DESC(power_save_controller, "Reset controller in power save mode."); -#endif +#endif /* CONFIG_PM */ static int align_buffer_size = -1; module_param(align_buffer_size, bint, 0644); @@ -406,6 +415,7 @@ struct azx_dev { */ unsigned int insufficient :1; unsigned int wc_marked:1; + unsigned int no_period_wakeup:1; }; /* CORB/RIRB */ @@ -471,6 +481,10 @@ struct azx { struct snd_dma_buffer rb; struct snd_dma_buffer posbuf; +#ifdef CONFIG_SND_HDA_PATCH_LOADER + const struct firmware *fw; +#endif + /* flags */ int position_fix[2]; /* for both playback/capture streams */ int poll_count; @@ -498,6 +512,9 @@ struct azx { /* reboot notifier (for mysterious hangup problem at power-down) */ struct notifier_block reboot_notifier; + + /* card list (for power_save trigger) */ + struct list_head list; }; /* driver types */ @@ -538,6 +555,7 @@ enum { #define AZX_DCAPS_ALIGN_BUFSIZE (1 << 22) /* buffer size alignment */ #define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23) /* BDLE in 4k boundary */ #define AZX_DCAPS_POSFIX_COMBO (1 << 24) /* Use COMBO as default */ +#define AZX_DCAPS_COUNT_LPIB_DELAY (1 << 25) /* Take LPIB as delay */ /* quirks for ATI SB / AMD Hudson */ #define AZX_DCAPS_PRESET_ATI_SB \ @@ -560,13 +578,17 @@ enum { * VGA-switcher support */ #ifdef SUPPORT_VGA_SWITCHEROO +#define use_vga_switcheroo(chip) ((chip)->use_vga_switcheroo) +#else +#define use_vga_switcheroo(chip) 0 +#endif + +#if defined(SUPPORT_VGA_SWITCHEROO) || defined(CONFIG_SND_HDA_PATCH_LOADER) #define DELAYED_INIT_MARK #define DELAYED_INITDATA_MARK -#define use_vga_switcheroo(chip) ((chip)->use_vga_switcheroo) #else #define DELAYED_INIT_MARK __devinit #define DELAYED_INITDATA_MARK __devinitdata -#define use_vga_switcheroo(chip) 0 #endif static char *driver_short_names[] DELAYED_INITDATA_MARK = { @@ -1012,8 +1034,8 @@ static unsigned int azx_get_response(struct hda_bus *bus, return azx_rirb_get_response(bus, addr); } -#ifdef CONFIG_SND_HDA_POWER_SAVE -static void azx_power_notify(struct hda_bus *bus); +#ifdef CONFIG_PM +static void azx_power_notify(struct hda_bus *bus, bool power_up); #endif /* reset codec link */ @@ -1269,6 +1291,11 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id) u8 sd_status; int i, ok; +#ifdef CONFIG_PM_RUNTIME + if (chip->pci->dev.power.runtime_status != RPM_ACTIVE) + return IRQ_NONE; +#endif + spin_lock(&chip->reg_lock); if (chip->disabled) { @@ -1394,7 +1421,7 @@ static int azx_setup_periods(struct azx *chip, ofs = 0; azx_dev->frags = 0; pos_adj = bdl_pos_adj[chip->dev_index]; - if (pos_adj > 0) { + if (!azx_dev->no_period_wakeup && pos_adj > 0) { struct snd_pcm_runtime *runtime = substream->runtime; int pos_align = pos_adj; pos_adj = (pos_adj * runtime->rate + 47999) / 48000; @@ -1410,8 +1437,7 @@ static int azx_setup_periods(struct azx *chip, pos_adj = 0; } else { ofs = setup_bdle(chip, substream, azx_dev, - &bdl, ofs, pos_adj, - !substream->runtime->no_period_wakeup); + &bdl, ofs, pos_adj, true); if (ofs < 0) goto error; } @@ -1424,7 +1450,7 @@ static int azx_setup_periods(struct azx *chip, else ofs = setup_bdle(chip, substream, azx_dev, &bdl, ofs, period_bytes, - !substream->runtime->no_period_wakeup); + !azx_dev->no_period_wakeup); if (ofs < 0) goto error; } @@ -1580,7 +1606,7 @@ static int DELAYED_INIT_MARK azx_codec_create(struct azx *chip, const char *mode bus_temp.ops.get_response = azx_get_response; bus_temp.ops.attach_pcm = azx_attach_pcm_stream; bus_temp.ops.bus_reset = azx_bus_reset; -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM bus_temp.power_save = &power_save; bus_temp.ops.pm_notify = azx_power_notify; #endif @@ -1897,10 +1923,12 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) if (bufsize != azx_dev->bufsize || period_bytes != azx_dev->period_bytes || - format_val != azx_dev->format_val) { + format_val != azx_dev->format_val || + runtime->no_period_wakeup != azx_dev->no_period_wakeup) { azx_dev->bufsize = bufsize; azx_dev->period_bytes = period_bytes; azx_dev->format_val = format_val; + azx_dev->no_period_wakeup = runtime->no_period_wakeup; err = azx_setup_periods(chip, substream, azx_dev); if (err < 0) return err; @@ -1959,14 +1987,14 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) } spin_lock(&chip->reg_lock); - if (nsync > 1) { - /* first, set SYNC bits of corresponding streams */ - if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC) - azx_writel(chip, OLD_SSYNC, - azx_readl(chip, OLD_SSYNC) | sbits); - else - azx_writel(chip, SSYNC, azx_readl(chip, SSYNC) | sbits); - } + + /* first, set SYNC bits of corresponding streams */ + if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC) + azx_writel(chip, OLD_SSYNC, + azx_readl(chip, OLD_SSYNC) | sbits); + else + azx_writel(chip, SSYNC, azx_readl(chip, SSYNC) | sbits); + snd_pcm_group_for_each_entry(s, substream) { if (s->pcm->card != substream->pcm->card) continue; @@ -1984,8 +2012,6 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) } spin_unlock(&chip->reg_lock); if (start) { - if (nsync == 1) - return 0; /* wait until all FIFOs get ready */ for (timeout = 5000; timeout; timeout--) { nwait = 0; @@ -2018,16 +2044,14 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) cpu_relax(); } } - if (nsync > 1) { - spin_lock(&chip->reg_lock); - /* reset SYNC bits */ - if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC) - azx_writel(chip, OLD_SSYNC, - azx_readl(chip, OLD_SSYNC) & ~sbits); - else - azx_writel(chip, SSYNC, azx_readl(chip, SSYNC) & ~sbits); - spin_unlock(&chip->reg_lock); - } + spin_lock(&chip->reg_lock); + /* reset SYNC bits */ + if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC) + azx_writel(chip, OLD_SSYNC, + azx_readl(chip, OLD_SSYNC) & ~sbits); + else + azx_writel(chip, SSYNC, azx_readl(chip, SSYNC) & ~sbits); + spin_unlock(&chip->reg_lock); return 0; } @@ -2120,6 +2144,27 @@ static unsigned int azx_get_position(struct azx *chip, if (pos >= azx_dev->bufsize) pos = 0; + + /* calculate runtime delay from LPIB */ + if (azx_dev->substream->runtime && + chip->position_fix[stream] == POS_FIX_POSBUF && + (chip->driver_caps & AZX_DCAPS_COUNT_LPIB_DELAY)) { + unsigned int lpib_pos = azx_sd_readl(azx_dev, SD_LPIB); + int delay; + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + delay = pos - lpib_pos; + else + delay = lpib_pos - pos; + if (delay < 0) + delay += azx_dev->bufsize; + if (delay >= azx_dev->period_bytes) { + snd_printdd("delay %d > period_bytes %d\n", + delay, azx_dev->period_bytes); + delay = 0; /* something is wrong */ + } + azx_dev->substream->runtime->delay = + bytes_to_frames(azx_dev->substream->runtime, delay); + } return pos; } @@ -2379,33 +2424,65 @@ static void azx_stop_chip(struct azx *chip) chip->initialized = 0; } -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM /* power-up/down the controller */ -static void azx_power_notify(struct hda_bus *bus) +static void azx_power_notify(struct hda_bus *bus, bool power_up) { struct azx *chip = bus->private_data; + + if (power_up) + pm_runtime_get_sync(&chip->pci->dev); + else + pm_runtime_put_sync(&chip->pci->dev); +} + +static DEFINE_MUTEX(card_list_lock); +static LIST_HEAD(card_list); + +static void azx_add_card_list(struct azx *chip) +{ + mutex_lock(&card_list_lock); + list_add(&chip->list, &card_list); + mutex_unlock(&card_list_lock); +} + +static void azx_del_card_list(struct azx *chip) +{ + mutex_lock(&card_list_lock); + list_del_init(&chip->list); + mutex_unlock(&card_list_lock); +} + +/* trigger power-save check at writing parameter */ +static int param_set_xint(const char *val, const struct kernel_param *kp) +{ + struct azx *chip; struct hda_codec *c; - int power_on = 0; + int prev = power_save; + int ret = param_set_int(val, kp); - list_for_each_entry(c, &bus->codec_list, list) { - if (c->power_on) { - power_on = 1; - break; - } + if (ret || prev == power_save) + return ret; + + mutex_lock(&card_list_lock); + list_for_each_entry(chip, &card_list, list) { + if (!chip->bus || chip->disabled) + continue; + list_for_each_entry(c, &chip->bus->codec_list, list) + snd_hda_power_sync(c); } - if (power_on) - azx_init_chip(chip, 1); - else if (chip->running && power_save_controller && - !bus->power_keep_link_on) - azx_stop_chip(chip); + mutex_unlock(&card_list_lock); + return 0; } -#endif /* CONFIG_SND_HDA_POWER_SAVE */ +#else +#define azx_add_card_list(chip) /* NOP */ +#define azx_del_card_list(chip) /* NOP */ +#endif /* CONFIG_PM */ -#ifdef CONFIG_PM +#if defined(CONFIG_PM_SLEEP) || defined(SUPPORT_VGA_SWITCHEROO) /* * power management */ - static int azx_suspend(struct device *dev) { struct pci_dev *pci = to_pci_dev(dev); @@ -2460,11 +2537,41 @@ static int azx_resume(struct device *dev) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } -static SIMPLE_DEV_PM_OPS(azx_pm, azx_suspend, azx_resume); +#endif /* CONFIG_PM_SLEEP || SUPPORT_VGA_SWITCHEROO */ + +#ifdef CONFIG_PM_RUNTIME +static int azx_runtime_suspend(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct azx *chip = card->private_data; + + if (!power_save_controller) + return -EAGAIN; + + azx_stop_chip(chip); + azx_clear_irq_pending(chip); + return 0; +} + +static int azx_runtime_resume(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct azx *chip = card->private_data; + + azx_init_pci(chip); + azx_init_chip(chip, 1); + return 0; +} +#endif /* CONFIG_PM_RUNTIME */ + +#ifdef CONFIG_PM +static const struct dev_pm_ops azx_pm = { + SET_SYSTEM_SLEEP_PM_OPS(azx_suspend, azx_resume) + SET_RUNTIME_PM_OPS(azx_runtime_suspend, azx_runtime_resume, NULL) +}; + #define AZX_PM_OPS &azx_pm #else -#define azx_suspend(dev) -#define azx_resume(dev) #define AZX_PM_OPS NULL #endif /* CONFIG_PM */ @@ -2599,6 +2706,8 @@ static int azx_free(struct azx *chip) { int i; + azx_del_card_list(chip); + azx_notifier_unregister(chip); if (use_vga_switcheroo(chip)) { @@ -2640,6 +2749,10 @@ static int azx_free(struct azx *chip) pci_release_regions(chip->pci); pci_disable_device(chip->pci); kfree(chip->azx_dev); +#ifdef CONFIG_SND_HDA_PATCH_LOADER + if (chip->fw) + release_firmware(chip->fw); +#endif kfree(chip); return 0; @@ -2719,6 +2832,7 @@ static int __devinit check_position_fix(struct azx *chip, int fix) const struct snd_pci_quirk *q; switch (fix) { + case POS_FIX_AUTO: case POS_FIX_LPIB: case POS_FIX_POSBUF: case POS_FIX_VIACOMBO: @@ -2904,6 +3018,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, chip->dev_index = dev; INIT_WORK(&chip->irq_pending_work, azx_irq_pending_work); INIT_LIST_HEAD(&chip->pcm_list); + INIT_LIST_HEAD(&chip->list); init_vga_switcheroo(chip); chip->position_fix[0] = chip->position_fix[1] = @@ -3138,7 +3253,7 @@ static int DELAYED_INIT_MARK azx_first_init(struct azx *chip) static void power_down_all_codecs(struct azx *chip) { -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM /* The codecs were powered up in snd_hda_codec_new(). * Now all initialization done, so turn them down if possible */ @@ -3149,12 +3264,40 @@ static void power_down_all_codecs(struct azx *chip) #endif } +#ifdef CONFIG_SND_HDA_PATCH_LOADER +/* callback from request_firmware_nowait() */ +static void azx_firmware_cb(const struct firmware *fw, void *context) +{ + struct snd_card *card = context; + struct azx *chip = card->private_data; + struct pci_dev *pci = chip->pci; + + if (!fw) { + snd_printk(KERN_ERR SFX "Cannot load firmware, aborting\n"); + goto error; + } + + chip->fw = fw; + if (!chip->disabled) { + /* continue probing */ + if (azx_probe_continue(chip)) + goto error; + } + return; /* OK */ + + error: + snd_card_free(card); + pci_set_drvdata(pci, NULL); +} +#endif + static int __devinit azx_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { static int dev; struct snd_card *card; struct azx *chip; + bool probe_now; int err; if (dev >= SNDRV_CARDS) @@ -3170,15 +3313,28 @@ static int __devinit azx_probe(struct pci_dev *pci, return err; } - /* set this here since it's referred in snd_hda_load_patch() */ snd_card_set_dev(card, &pci->dev); err = azx_create(card, pci, dev, pci_id->driver_data, &chip); if (err < 0) goto out_free; card->private_data = chip; + probe_now = !chip->disabled; - if (!chip->disabled) { +#ifdef CONFIG_SND_HDA_PATCH_LOADER + if (patch[dev] && *patch[dev]) { + snd_printk(KERN_ERR SFX "Applying patch firmware '%s'\n", + patch[dev]); + err = request_firmware_nowait(THIS_MODULE, true, patch[dev], + &pci->dev, GFP_KERNEL, card, + azx_firmware_cb); + if (err < 0) + goto out_free; + probe_now = false; /* continued in azx_firmware_cb() */ + } +#endif /* CONFIG_SND_HDA_PATCH_LOADER */ + + if (probe_now) { err = azx_probe_continue(chip); if (err < 0) goto out_free; @@ -3186,6 +3342,9 @@ static int __devinit azx_probe(struct pci_dev *pci, pci_set_drvdata(pci, card); + if (pci_dev_run_wake(pci)) + pm_runtime_put_noidle(&pci->dev); + dev++; return 0; @@ -3208,12 +3367,13 @@ static int DELAYED_INIT_MARK azx_probe_continue(struct azx *chip) if (err < 0) goto out_free; #ifdef CONFIG_SND_HDA_PATCH_LOADER - if (patch[dev] && *patch[dev]) { - snd_printk(KERN_ERR SFX "Applying patch firmware '%s'\n", - patch[dev]); - err = snd_hda_load_patch(chip->bus, patch[dev]); + if (chip->fw) { + err = snd_hda_load_patch(chip->bus, chip->fw->size, + chip->fw->data); if (err < 0) goto out_free; + release_firmware(chip->fw); /* no longer needed */ + chip->fw = NULL; } #endif if ((probe_only[dev] & 1) == 0) { @@ -3239,6 +3399,7 @@ static int DELAYED_INIT_MARK azx_probe_continue(struct azx *chip) chip->running = 1; power_down_all_codecs(chip); azx_notifier_register(chip); + azx_add_card_list(chip); return 0; @@ -3250,6 +3411,10 @@ out_free: static void __devexit azx_remove(struct pci_dev *pci) { struct snd_card *card = pci_get_drvdata(pci); + + if (pci_dev_run_wake(pci)) + pm_runtime_get_noresume(&pci->dev); + if (card) snd_card_free(card); pci_set_drvdata(pci, NULL); @@ -3260,7 +3425,7 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = { /* CPT */ { PCI_DEVICE(0x8086, 0x1c20), .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP | - AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_COMBO }, + AZX_DCAPS_BUFSIZE | AZX_DCAPS_COUNT_LPIB_DELAY }, /* PBG */ { PCI_DEVICE(0x8086, 0x1d20), .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP | @@ -3268,23 +3433,30 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = { /* Panther Point */ { PCI_DEVICE(0x8086, 0x1e20), .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP | - AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_COMBO }, + AZX_DCAPS_BUFSIZE | AZX_DCAPS_COUNT_LPIB_DELAY }, /* Lynx Point */ { PCI_DEVICE(0x8086, 0x8c20), .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP | - AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_COMBO }, + AZX_DCAPS_BUFSIZE | AZX_DCAPS_COUNT_LPIB_DELAY }, /* Lynx Point-LP */ { PCI_DEVICE(0x8086, 0x9c20), .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP | - AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_COMBO }, + AZX_DCAPS_BUFSIZE | AZX_DCAPS_COUNT_LPIB_DELAY }, /* Lynx Point-LP */ { PCI_DEVICE(0x8086, 0x9c21), .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP | - AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_COMBO }, + AZX_DCAPS_BUFSIZE | AZX_DCAPS_COUNT_LPIB_DELAY }, /* Haswell */ { PCI_DEVICE(0x8086, 0x0c0c), .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_SCH_SNOOP | - AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_COMBO }, + AZX_DCAPS_BUFSIZE | AZX_DCAPS_COUNT_LPIB_DELAY }, + { PCI_DEVICE(0x8086, 0x0d0c), + .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_SCH_SNOOP | + AZX_DCAPS_BUFSIZE | AZX_DCAPS_COUNT_LPIB_DELAY }, + /* 5 Series/3400 */ + { PCI_DEVICE(0x8086, 0x3b56), + .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_SCH_SNOOP | + AZX_DCAPS_BUFSIZE | AZX_DCAPS_COUNT_LPIB_DELAY }, /* SCH */ { PCI_DEVICE(0x8086, 0x811b), .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_SCH_SNOOP | diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index aaccc02..5c690cb 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -26,9 +26,8 @@ bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid) return false; if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_PRES_DETECT)) return false; - if (!codec->ignore_misc_bit && - (get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) & - AC_DEFCFG_MISC_NO_PRESENCE)) + if (get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) & + AC_DEFCFG_MISC_NO_PRESENCE) return false; if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP)) return false; @@ -193,8 +192,9 @@ EXPORT_SYMBOL_HDA(snd_hda_jack_detect); /** * snd_hda_jack_detect_enable - enable the jack-detection */ -int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid, - unsigned char action) +int snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid, + unsigned char action, + hda_jack_callback cb) { struct hda_jack_tbl *jack = snd_hda_jack_tbl_new(codec, nid); if (!jack) @@ -204,10 +204,19 @@ int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid, jack->jack_detect = 1; if (action) jack->action = action; + if (cb) + jack->callback = cb; return snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | jack->tag); } +EXPORT_SYMBOL_HDA(snd_hda_jack_detect_enable_callback); + +int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid, + unsigned char action) +{ + return snd_hda_jack_detect_enable_callback(codec, nid, action, NULL); +} EXPORT_SYMBOL_HDA(snd_hda_jack_detect_enable); /** @@ -412,3 +421,21 @@ int snd_hda_jack_add_kctls(struct hda_codec *codec, return 0; } EXPORT_SYMBOL_HDA(snd_hda_jack_add_kctls); + +void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res) +{ + struct hda_jack_tbl *event; + int tag = (res >> AC_UNSOL_RES_TAG_SHIFT) & 0x7f; + + event = snd_hda_jack_tbl_get_from_tag(codec, tag); + if (!event) + return; + event->jack_dirty = 1; + + if (event->callback) + event->callback(codec, event); + + snd_hda_jack_report_sync(codec); +} +EXPORT_SYMBOL_HDA(snd_hda_jack_unsol_event); + diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h index a9803da..af8dd47 100644 --- a/sound/pci/hda/hda_jack.h +++ b/sound/pci/hda/hda_jack.h @@ -13,12 +13,16 @@ #define __SOUND_HDA_JACK_H struct auto_pin_cfg; +struct hda_jack_tbl; + +typedef void (*hda_jack_callback) (struct hda_codec *, struct hda_jack_tbl *); struct hda_jack_tbl { hda_nid_t nid; unsigned char action; /* event action (0 = none) */ unsigned char tag; /* unsol event tag */ unsigned int private_data; /* arbitrary data */ + hda_jack_callback callback; /* jack-detection stuff */ unsigned int pin_sense; /* cached pin-sense value */ unsigned int jack_detect:1; /* capable of jack-detection? */ @@ -61,6 +65,10 @@ void snd_hda_jack_set_dirty_all(struct hda_codec *codec); int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid, unsigned char action); +int snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid, + unsigned char action, + hda_jack_callback cb); + u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid); int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid); @@ -74,5 +82,6 @@ int snd_hda_jack_add_kctls(struct hda_codec *codec, void snd_hda_jack_report_sync(struct hda_codec *codec); +void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res); #endif /* __SOUND_HDA_JACK_H */ diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 1b4c129..09dbdc3 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -529,7 +529,7 @@ int snd_hda_create_hwdep(struct hda_codec *codec); static inline int snd_hda_create_hwdep(struct hda_codec *codec) { return 0; } #endif -#if defined(CONFIG_SND_HDA_POWER_SAVE) && defined(CONFIG_SND_HDA_HWDEP) +#if defined(CONFIG_PM) && defined(CONFIG_SND_HDA_HWDEP) int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec); #else static inline int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec) diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index 6894ec6..045e5d3 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -402,6 +402,9 @@ static void print_digital_conv(struct snd_info_buffer *buffer, { unsigned int digi1 = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT_1, 0); + unsigned char digi2 = digi1 >> 8; + unsigned char digi3 = digi1 >> 16; + snd_iprintf(buffer, " Digital:"); if (digi1 & AC_DIG1_ENABLE) snd_iprintf(buffer, " Enabled"); @@ -419,9 +422,13 @@ static void print_digital_conv(struct snd_info_buffer *buffer, snd_iprintf(buffer, " Pro"); if (digi1 & AC_DIG1_LEVEL) snd_iprintf(buffer, " GenLevel"); + if (digi3 & AC_DIG3_KAE) + snd_iprintf(buffer, " KAE"); snd_iprintf(buffer, "\n"); snd_iprintf(buffer, " Digital category: 0x%x\n", - (digi1 >> 8) & AC_DIG2_CC); + digi2 & AC_DIG2_CC); + snd_iprintf(buffer, " IEC Coding Type: 0x%x\n", + digi3 & AC_DIG3_ICT); } static const char *get_pwr_state(u32 state) diff --git a/sound/pci/hda/hda_trace.h b/sound/pci/hda/hda_trace.h index 9884871..3a1c631 100644 --- a/sound/pci/hda/hda_trace.h +++ b/sound/pci/hda/hda_trace.h @@ -58,6 +58,7 @@ TRACE_EVENT(hda_bus_reset, TP_printk("[%d]", __entry->card) ); +#ifdef CONFIG_PM DECLARE_EVENT_CLASS(hda_power, TP_PROTO(struct hda_codec *codec), @@ -87,6 +88,31 @@ DEFINE_EVENT(hda_power, hda_power_up, TP_ARGS(codec) ); +TRACE_EVENT(hda_power_count, + TP_PROTO(struct hda_codec *codec), + TP_ARGS(codec), + TP_STRUCT__entry( + __field( unsigned int, card ) + __field( unsigned int, addr ) + __field( int, power_count ) + __field( int, power_on ) + __field( int, power_transition ) + ), + + TP_fast_assign( + __entry->card = (codec)->bus->card->number; + __entry->addr = (codec)->addr; + __entry->power_count = (codec)->power_count; + __entry->power_on = (codec)->power_on; + __entry->power_transition = (codec)->power_transition; + ), + + TP_printk("[%d:%d] power_count=%d, power_on=%d, power_transition=%d", + __entry->card, __entry->addr, __entry->power_count, + __entry->power_on, __entry->power_transition) +); +#endif /* CONFIG_PM */ + TRACE_EVENT(hda_unsol_event, TP_PROTO(struct hda_bus *bus, u32 res, u32 res_ex), diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 0208fa1..cdd43ea 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -85,7 +85,7 @@ struct ad198x_spec { unsigned int analog_beep: 1; /* analog beep input present */ unsigned int avoid_init_slave_vol:1; -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM struct hda_loopback_check loopback; #endif /* for virtual master */ @@ -269,7 +269,7 @@ static int ad198x_build_controls(struct hda_codec *codec) return 0; } -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid) { struct ad198x_spec *spec = codec->spec; @@ -654,10 +654,8 @@ static const struct hda_codec_ops ad198x_patch_ops = { .build_pcms = ad198x_build_pcms, .init = ad198x_init, .free = ad198x_free, -#ifdef CONFIG_SND_HDA_POWER_SAVE - .check_power_status = ad198x_check_power_status, -#endif #ifdef CONFIG_PM + .check_power_status = ad198x_check_power_status, .suspend = ad198x_suspend, #endif .reboot_notify = ad198x_shutup, @@ -1231,7 +1229,7 @@ static const struct snd_pci_quirk ad1986a_cfg_tbl[] = { {} }; -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM static const struct hda_amp_list ad1986a_loopbacks[] = { { 0x13, HDA_OUTPUT, 0 }, /* Mic */ { 0x14, HDA_OUTPUT, 0 }, /* Phone */ @@ -1278,7 +1276,7 @@ static int patch_ad1986a(struct hda_codec *codec) spec->mixers[0] = ad1986a_mixers; spec->num_init_verbs = 1; spec->init_verbs[0] = ad1986a_init_verbs; -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM spec->loopback.amplist = ad1986a_loopbacks; #endif spec->vmaster_nid = 0x1b; @@ -1537,7 +1535,7 @@ static const struct hda_verb ad1983_init_verbs[] = { { } /* end */ }; -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM static const struct hda_amp_list ad1983_loopbacks[] = { { 0x12, HDA_OUTPUT, 0 }, /* Mic */ { 0x13, HDA_OUTPUT, 0 }, /* Line */ @@ -1576,7 +1574,7 @@ static int patch_ad1983(struct hda_codec *codec) spec->num_init_verbs = 1; spec->init_verbs[0] = ad1983_init_verbs; spec->spdif_route = 0; -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM spec->loopback.amplist = ad1983_loopbacks; #endif spec->vmaster_nid = 0x05; @@ -1704,7 +1702,7 @@ static const struct hda_verb ad1981_init_verbs[] = { { } /* end */ }; -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM static const struct hda_amp_list ad1981_loopbacks[] = { { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */ { 0x13, HDA_OUTPUT, 0 }, /* Line */ @@ -1812,7 +1810,7 @@ static const struct hda_input_mux ad1981_hp_capture_source = { .num_items = 3, .items = { { "Mic", 0x0 }, - { "Docking-Station", 0x1 }, + { "Dock Mic", 0x1 }, { "Mix", 0x2 }, }, }; @@ -1836,8 +1834,8 @@ static const struct snd_kcontrol_new ad1981_hp_mixers[] = { */ HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Docking-Station Playback Volume", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Docking-Station Playback Switch", 0x13, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT), /* FIXME: does this laptop have analog CD connection? */ @@ -1982,7 +1980,7 @@ static int patch_ad1981(struct hda_codec *codec) spec->num_init_verbs = 1; spec->init_verbs[0] = ad1981_init_verbs; spec->spdif_route = 0; -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM spec->loopback.amplist = ad1981_loopbacks; #endif spec->vmaster_nid = 0x05; @@ -2807,7 +2805,7 @@ static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res) snd_hda_sequence_write(codec, ad1988_laptop_hp_off); } -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM static const struct hda_amp_list ad1988_loopbacks[] = { { 0x20, HDA_INPUT, 0 }, /* Front Mic */ { 0x20, HDA_INPUT, 1 }, /* Line */ @@ -3399,7 +3397,7 @@ static int patch_ad1988(struct hda_codec *codec) codec->patch_ops.unsol_event = ad1988_laptop_unsol_event; break; } -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM spec->loopback.amplist = ad1988_loopbacks; #endif spec->vmaster_nid = 0x04; @@ -3555,7 +3553,7 @@ static const struct hda_verb ad1884_init_verbs[] = { { } /* end */ }; -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM static const struct hda_amp_list ad1884_loopbacks[] = { { 0x20, HDA_INPUT, 0 }, /* Front Mic */ { 0x20, HDA_INPUT, 1 }, /* Mic */ @@ -3567,7 +3565,7 @@ static const struct hda_amp_list ad1884_loopbacks[] = { static const char * const ad1884_slave_vols[] = { "PCM", "Mic", "Mono", "Front Mic", "Mic", "CD", - "Internal Mic", "Docking Mic", /* "Beep", */ "IEC958", + "Internal Mic", "Dock Mic", /* "Beep", */ "IEC958", NULL }; @@ -3602,7 +3600,7 @@ static int patch_ad1884(struct hda_codec *codec) spec->num_init_verbs = 1; spec->init_verbs[0] = ad1884_init_verbs; spec->spdif_route = 0; -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM spec->loopback.amplist = ad1884_loopbacks; #endif spec->vmaster_nid = 0x04; @@ -3628,7 +3626,7 @@ static const struct hda_input_mux ad1984_thinkpad_capture_source = { { "Mic", 0x0 }, { "Internal Mic", 0x1 }, { "Mix", 0x3 }, - { "Docking-Station", 0x4 }, + { "Dock Mic", 0x4 }, }, }; @@ -3657,8 +3655,8 @@ static const struct snd_kcontrol_new ad1984_thinkpad_mixers[] = { HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT), HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT), HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT), - HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT), HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x15, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT), @@ -3994,7 +3992,7 @@ static const struct hda_verb ad1884a_init_verbs[] = { { } /* end */ }; -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM static const struct hda_amp_list ad1884a_loopbacks[] = { { 0x20, HDA_INPUT, 0 }, /* Front Mic */ { 0x20, HDA_INPUT, 1 }, /* Mic */ @@ -4602,7 +4600,7 @@ static int patch_ad1884a(struct hda_codec *codec) spec->num_init_verbs = 1; spec->init_verbs[0] = ad1884a_init_verbs; spec->spdif_route = 0; -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM spec->loopback.amplist = ad1884a_loopbacks; #endif codec->patch_ops = ad198x_patch_ops; @@ -4814,6 +4812,32 @@ static const struct snd_kcontrol_new ad1882_3stack_mixers[] = { { } /* end */ }; +/* simple auto-mute control for AD1882 3-stack board */ +#define AD1882_HP_EVENT 0x01 + +static void ad1882_3stack_automute(struct hda_codec *codec) +{ + bool mute = snd_hda_jack_detect(codec, 0x11); + snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + mute ? 0 : PIN_OUT); +} + +static int ad1882_3stack_automute_init(struct hda_codec *codec) +{ + ad198x_init(codec); + ad1882_3stack_automute(codec); + return 0; +} + +static void ad1882_3stack_unsol_event(struct hda_codec *codec, unsigned int res) +{ + switch (res >> 26) { + case AD1882_HP_EVENT: + ad1882_3stack_automute(codec); + break; + } +} + static const struct snd_kcontrol_new ad1882_6stack_mixers[] = { HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT), @@ -4928,7 +4952,12 @@ static const struct hda_verb ad1882_init_verbs[] = { { } /* end */ }; -#ifdef CONFIG_SND_HDA_POWER_SAVE +static const struct hda_verb ad1882_3stack_automute_verbs[] = { + {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1882_HP_EVENT}, + { } /* end */ +}; + +#ifdef CONFIG_PM static const struct hda_amp_list ad1882_loopbacks[] = { { 0x20, HDA_INPUT, 0 }, /* Front Mic */ { 0x20, HDA_INPUT, 1 }, /* Mic */ @@ -4942,12 +4971,14 @@ static const struct hda_amp_list ad1882_loopbacks[] = { enum { AD1882_3STACK, AD1882_6STACK, + AD1882_3STACK_AUTOMUTE, AD1882_MODELS }; static const char * const ad1882_models[AD1986A_MODELS] = { [AD1882_3STACK] = "3stack", [AD1882_6STACK] = "6stack", + [AD1882_3STACK_AUTOMUTE] = "3stack-automute", }; @@ -4989,7 +5020,7 @@ static int patch_ad1882(struct hda_codec *codec) spec->num_init_verbs = 1; spec->init_verbs[0] = ad1882_init_verbs; spec->spdif_route = 0; -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM spec->loopback.amplist = ad1882_loopbacks; #endif spec->vmaster_nid = 0x04; @@ -5002,6 +5033,7 @@ static int patch_ad1882(struct hda_codec *codec) switch (board_config) { default: case AD1882_3STACK: + case AD1882_3STACK_AUTOMUTE: spec->num_mixers = 3; spec->mixers[2] = ad1882_3stack_mixers; spec->channel_mode = ad1882_modes; @@ -5009,6 +5041,12 @@ static int patch_ad1882(struct hda_codec *codec) spec->need_dac_fix = 1; spec->multiout.max_channels = 2; spec->multiout.num_dacs = 1; + if (board_config != AD1882_3STACK) { + spec->init_verbs[spec->num_init_verbs++] = + ad1882_3stack_automute_verbs; + codec->patch_ops.unsol_event = ad1882_3stack_unsol_event; + codec->patch_ops.init = ad1882_3stack_automute_init; + } break; case AD1882_6STACK: spec->num_mixers = 3; diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 0c4c1a6..fcfc9f0 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -34,7 +34,8 @@ */ struct cs_spec { - int board_config; + struct hda_gen_spec gen; + struct auto_pin_cfg autocfg; struct hda_multi_out multiout; struct snd_kcontrol *vmaster_sw; @@ -80,16 +81,20 @@ enum { CS420X_MBP53, CS420X_MBP55, CS420X_IMAC27, - CS420X_IMAC27_122, - CS420X_APPLE, + CS420X_GPIO_13, + CS420X_GPIO_23, + CS420X_MBP101, + CS420X_MBP101_COEF, CS420X_AUTO, - CS420X_MODELS + /* aliases */ + CS420X_IMAC27_122 = CS420X_GPIO_23, + CS420X_APPLE = CS420X_GPIO_13, }; /* CS421x boards */ enum { CS421X_CDB4210, - CS421X_MODELS + CS421X_SENSE_B, }; /* Vendor-specific processing widget */ @@ -1157,6 +1162,14 @@ static const struct hda_verb cs_errata_init_verbs[] = { {} /* terminator */ }; +static const struct hda_verb mbp101_init_verbs[] = { + {0x11, AC_VERB_SET_COEF_INDEX, 0x0002}, + {0x11, AC_VERB_SET_PROC_COEF, 0x100a}, + {0x11, AC_VERB_SET_COEF_INDEX, 0x0004}, + {0x11, AC_VERB_SET_PROC_COEF, 0x000f}, + {} +}; + /* SPDIF setup */ static void init_digital(struct hda_codec *codec) { @@ -1193,7 +1206,6 @@ static int cs_init(struct hda_codec *codec) init_output(codec); init_input(codec); init_digital(codec); - snd_hda_jack_report_sync(codec); return 0; } @@ -1279,38 +1291,32 @@ static int cs_parse_auto_config(struct hda_codec *codec) return 0; } -static const char * const cs420x_models[CS420X_MODELS] = { - [CS420X_MBP53] = "mbp53", - [CS420X_MBP55] = "mbp55", - [CS420X_IMAC27] = "imac27", - [CS420X_IMAC27_122] = "imac27_122", - [CS420X_APPLE] = "apple", - [CS420X_AUTO] = "auto", +static const struct hda_model_fixup cs420x_models[] = { + { .id = CS420X_MBP53, .name = "mbp53" }, + { .id = CS420X_MBP55, .name = "mbp55" }, + { .id = CS420X_IMAC27, .name = "imac27" }, + { .id = CS420X_IMAC27_122, .name = "imac27_122" }, + { .id = CS420X_APPLE, .name = "apple" }, + { .id = CS420X_MBP101, .name = "mbp101" }, + {} }; - -static const struct snd_pci_quirk cs420x_cfg_tbl[] = { +static const struct snd_pci_quirk cs420x_fixup_tbl[] = { SND_PCI_QUIRK(0x10de, 0x0ac0, "MacBookPro 5,3", CS420X_MBP53), SND_PCI_QUIRK(0x10de, 0x0d94, "MacBookAir 3,1(2)", CS420X_MBP55), SND_PCI_QUIRK(0x10de, 0xcb79, "MacBookPro 5,5", CS420X_MBP55), SND_PCI_QUIRK(0x10de, 0xcb89, "MacBookPro 7,1", CS420X_MBP55), /* this conflicts with too many other models */ /*SND_PCI_QUIRK(0x8086, 0x7270, "IMac 27 Inch", CS420X_IMAC27),*/ - {} /* terminator */ -}; -static const struct snd_pci_quirk cs420x_codec_cfg_tbl[] = { + /* codec SSID */ SND_PCI_QUIRK(0x106b, 0x2000, "iMac 12,2", CS420X_IMAC27_122), + SND_PCI_QUIRK(0x106b, 0x2800, "MacBookPro 10,1", CS420X_MBP101), SND_PCI_QUIRK_VENDOR(0x106b, "Apple", CS420X_APPLE), {} /* terminator */ }; -struct cs_pincfg { - hda_nid_t nid; - u32 val; -}; - -static const struct cs_pincfg mbp53_pincfgs[] = { +static const struct hda_pintbl mbp53_pincfgs[] = { { 0x09, 0x012b4050 }, { 0x0a, 0x90100141 }, { 0x0b, 0x90100140 }, @@ -1324,7 +1330,7 @@ static const struct cs_pincfg mbp53_pincfgs[] = { {} /* terminator */ }; -static const struct cs_pincfg mbp55_pincfgs[] = { +static const struct hda_pintbl mbp55_pincfgs[] = { { 0x09, 0x012b4030 }, { 0x0a, 0x90100121 }, { 0x0b, 0x90100120 }, @@ -1338,7 +1344,7 @@ static const struct cs_pincfg mbp55_pincfgs[] = { {} /* terminator */ }; -static const struct cs_pincfg imac27_pincfgs[] = { +static const struct hda_pintbl imac27_pincfgs[] = { { 0x09, 0x012b4050 }, { 0x0a, 0x90100140 }, { 0x0b, 0x90100142 }, @@ -1352,22 +1358,78 @@ static const struct cs_pincfg imac27_pincfgs[] = { {} /* terminator */ }; -static const struct cs_pincfg *cs_pincfgs[CS420X_MODELS] = { - [CS420X_MBP53] = mbp53_pincfgs, - [CS420X_MBP55] = mbp55_pincfgs, - [CS420X_IMAC27] = imac27_pincfgs, +static const struct hda_pintbl mbp101_pincfgs[] = { + { 0x0d, 0x40ab90f0 }, + { 0x0e, 0x90a600f0 }, + { 0x12, 0x50a600f0 }, + {} /* terminator */ }; -static void fix_pincfg(struct hda_codec *codec, int model, - const struct cs_pincfg **pin_configs) +static void cs420x_fixup_gpio_13(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - const struct cs_pincfg *cfg = pin_configs[model]; - if (!cfg) - return; - for (; cfg->nid; cfg++) - snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val); + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + struct cs_spec *spec = codec->spec; + spec->gpio_eapd_hp = 2; /* GPIO1 = headphones */ + spec->gpio_eapd_speaker = 8; /* GPIO3 = speakers */ + spec->gpio_mask = spec->gpio_dir = + spec->gpio_eapd_hp | spec->gpio_eapd_speaker; + } } +static void cs420x_fixup_gpio_23(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + struct cs_spec *spec = codec->spec; + spec->gpio_eapd_hp = 4; /* GPIO2 = headphones */ + spec->gpio_eapd_speaker = 8; /* GPIO3 = speakers */ + spec->gpio_mask = spec->gpio_dir = + spec->gpio_eapd_hp | spec->gpio_eapd_speaker; + } +} + +static const struct hda_fixup cs420x_fixups[] = { + [CS420X_MBP53] = { + .type = HDA_FIXUP_PINS, + .v.pins = mbp53_pincfgs, + .chained = true, + .chain_id = CS420X_APPLE, + }, + [CS420X_MBP55] = { + .type = HDA_FIXUP_PINS, + .v.pins = mbp55_pincfgs, + .chained = true, + .chain_id = CS420X_GPIO_13, + }, + [CS420X_IMAC27] = { + .type = HDA_FIXUP_PINS, + .v.pins = imac27_pincfgs, + .chained = true, + .chain_id = CS420X_GPIO_13, + }, + [CS420X_GPIO_13] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs420x_fixup_gpio_13, + }, + [CS420X_GPIO_23] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs420x_fixup_gpio_23, + }, + [CS420X_MBP101] = { + .type = HDA_FIXUP_PINS, + .v.pins = mbp101_pincfgs, + .chained = true, + .chain_id = CS420X_MBP101_COEF, + }, + [CS420X_MBP101_COEF] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = mbp101_init_verbs, + .chained = true, + .chain_id = CS420X_GPIO_13, + }, +}; + static int patch_cs420x(struct hda_codec *codec) { struct cs_spec *spec; @@ -1380,33 +1442,9 @@ static int patch_cs420x(struct hda_codec *codec) spec->vendor_nid = CS420X_VENDOR_NID; - spec->board_config = - snd_hda_check_board_config(codec, CS420X_MODELS, - cs420x_models, cs420x_cfg_tbl); - if (spec->board_config < 0) - spec->board_config = - snd_hda_check_board_codec_sid_config(codec, - CS420X_MODELS, NULL, cs420x_codec_cfg_tbl); - if (spec->board_config >= 0) - fix_pincfg(codec, spec->board_config, cs_pincfgs); - - switch (spec->board_config) { - case CS420X_IMAC27: - case CS420X_MBP53: - case CS420X_MBP55: - case CS420X_APPLE: - spec->gpio_eapd_hp = 2; /* GPIO1 = headphones */ - spec->gpio_eapd_speaker = 8; /* GPIO3 = speakers */ - spec->gpio_mask = spec->gpio_dir = - spec->gpio_eapd_hp | spec->gpio_eapd_speaker; - break; - case CS420X_IMAC27_122: - spec->gpio_eapd_hp = 4; /* GPIO2 = headphones */ - spec->gpio_eapd_speaker = 8; /* GPIO3 = speakers */ - spec->gpio_mask = spec->gpio_dir = - spec->gpio_eapd_hp | spec->gpio_eapd_speaker; - break; - } + snd_hda_pick_fixup(codec, cs420x_models, cs420x_fixup_tbl, + cs420x_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); err = cs_parse_auto_config(codec); if (err < 0) @@ -1414,6 +1452,8 @@ static int patch_cs420x(struct hda_codec *codec) codec->patch_ops = cs_patch_ops; + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + return 0; error: @@ -1431,11 +1471,12 @@ static int patch_cs420x(struct hda_codec *codec) */ /* CS4210 board names */ -static const char *cs421x_models[CS421X_MODELS] = { - [CS421X_CDB4210] = "cdb4210", +static const struct hda_model_fixup cs421x_models[] = { + { .id = CS421X_CDB4210, .name = "cdb4210" }, + {} }; -static const struct snd_pci_quirk cs421x_cfg_tbl[] = { +static const struct snd_pci_quirk cs421x_fixup_tbl[] = { /* Test Intel board + CDB2410 */ SND_PCI_QUIRK(0x8086, 0x5001, "DP45SG/CDB4210", CS421X_CDB4210), {} /* terminator */ @@ -1443,7 +1484,7 @@ static const struct snd_pci_quirk cs421x_cfg_tbl[] = { /* CS4210 board pinconfigs */ /* Default CS4210 (CDB4210)*/ -static const struct cs_pincfg cdb4210_pincfgs[] = { +static const struct hda_pintbl cdb4210_pincfgs[] = { { 0x05, 0x0321401f }, { 0x06, 0x90170010 }, { 0x07, 0x03813031 }, @@ -1453,8 +1494,26 @@ static const struct cs_pincfg cdb4210_pincfgs[] = { {} /* terminator */ }; -static const struct cs_pincfg *cs421x_pincfgs[CS421X_MODELS] = { - [CS421X_CDB4210] = cdb4210_pincfgs, +/* Setup GPIO/SENSE for each board (if used) */ +static void cs421x_fixup_sense_b(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct cs_spec *spec = codec->spec; + if (action == HDA_FIXUP_ACT_PRE_PROBE) + spec->sense_b = 1; +} + +static const struct hda_fixup cs421x_fixups[] = { + [CS421X_CDB4210] = { + .type = HDA_FIXUP_PINS, + .v.pins = cdb4210_pincfgs, + .chained = true, + .chain_id = CS421X_SENSE_B, + }, + [CS421X_SENSE_B] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs421x_fixup_sense_b, + } }; static const struct hda_verb cs421x_coef_init_verbs[] = { @@ -1643,7 +1702,6 @@ static int cs421x_init(struct hda_codec *codec) init_output(codec); init_input(codec); init_cs421x_digital(codec); - snd_hda_jack_report_sync(codec); return 0; } @@ -1937,26 +1995,9 @@ static int patch_cs4210(struct hda_codec *codec) spec->vendor_nid = CS4210_VENDOR_NID; - spec->board_config = - snd_hda_check_board_config(codec, CS421X_MODELS, - cs421x_models, cs421x_cfg_tbl); - if (spec->board_config >= 0) - fix_pincfg(codec, spec->board_config, cs421x_pincfgs); - /* - Setup GPIO/SENSE for each board (if used) - */ - switch (spec->board_config) { - case CS421X_CDB4210: - snd_printd("CS4210 board: %s\n", - cs421x_models[spec->board_config]); -/* spec->gpio_mask = 3; - spec->gpio_dir = 3; - spec->gpio_data = 3; -*/ - spec->sense_b = 1; - - break; - } + snd_hda_pick_fixup(codec, cs421x_models, cs421x_fixup_tbl, + cs421x_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); /* Update the GPIO/DMIC/SENSE_B pinmux before the configuration @@ -1971,6 +2012,8 @@ static int patch_cs4210(struct hda_codec *codec) codec->patch_ops = cs421x_patch_ops; + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + return 0; error: diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 5e22a8f..03b1dc3 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -553,7 +553,7 @@ static int conexant_build_controls(struct hda_codec *codec) return 0; } -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM static int conexant_suspend(struct hda_codec *codec) { snd_hda_shutup_pins(codec); @@ -567,7 +567,7 @@ static const struct hda_codec_ops conexant_patch_ops = { .init = conexant_init, .free = conexant_free, .set_power_state = conexant_set_power, -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM .suspend = conexant_suspend, #endif .reboot_notify = snd_hda_shutup_pins, @@ -1710,8 +1710,8 @@ static const struct snd_kcontrol_new cxt5051_capture_mixers[] = { HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT), HDA_CODEC_VOLUME("Mic Volume", 0x14, 0x01, HDA_INPUT), HDA_CODEC_MUTE("Mic Switch", 0x14, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Docking Mic Volume", 0x15, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Docking Mic Switch", 0x15, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Dock Mic Volume", 0x15, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Dock Mic Switch", 0x15, 0x00, HDA_INPUT), {} }; @@ -3402,7 +3402,7 @@ static void cx_auto_update_speakers(struct hda_codec *codec) do_automute(codec, cfg->line_outs, cfg->line_out_pins, on); } -static void cx_auto_hp_automute(struct hda_codec *codec) +static void cx_auto_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) { struct conexant_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; @@ -3413,7 +3413,7 @@ static void cx_auto_hp_automute(struct hda_codec *codec) cx_auto_update_speakers(codec); } -static void cx_auto_line_automute(struct hda_codec *codec) +static void cx_auto_line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) { struct conexant_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; @@ -3540,8 +3540,9 @@ static int __select_input_connection(struct hda_codec *codec, hda_nid_t mux, hda_nid_t pin, hda_nid_t *srcp, bool do_select, int depth) { + struct conexant_spec *spec = codec->spec; hda_nid_t conn[HDA_MAX_NUM_INPUTS]; - int i, nums; + int startidx, i, nums; switch (get_wcaps_type(get_wcaps(codec, mux))) { case AC_WID_AUD_IN: @@ -3565,14 +3566,25 @@ static int __select_input_connection(struct hda_codec *codec, hda_nid_t mux, depth++; if (depth == 2) return -1; + + /* Try to rotate around connections to avoid one boost controlling + another input path as well */ + startidx = 0; + for (i = 0; i < spec->private_imux.num_items; i++) + if (spec->imux_info[i].pin == pin) { + startidx = i; + break; + } + for (i = 0; i < nums; i++) { - int ret = __select_input_connection(codec, conn[i], pin, srcp, + int j = (i + startidx) % nums; + int ret = __select_input_connection(codec, conn[j], pin, srcp, do_select, depth); if (ret >= 0) { if (do_select) snd_hda_codec_write(codec, mux, 0, - AC_VERB_SET_CONNECT_SEL, i); - return i; + AC_VERB_SET_CONNECT_SEL, j); + return j; } } return -1; @@ -3652,7 +3664,7 @@ static bool select_automic(struct hda_codec *codec, int idx, bool detect) } /* automatic switch internal and external mic */ -static void cx_auto_automic(struct hda_codec *codec) +static void cx_auto_automic(struct hda_codec *codec, struct hda_jack_tbl *jack) { struct conexant_spec *spec = codec->spec; @@ -3663,22 +3675,6 @@ static void cx_auto_automic(struct hda_codec *codec) select_automic(codec, spec->auto_mic_int, false); } -static void cx_auto_unsol_event(struct hda_codec *codec, unsigned int res) -{ - switch (snd_hda_jack_get_action(codec, res >> 26)) { - case CONEXANT_HP_EVENT: - cx_auto_hp_automute(codec); - break; - case CONEXANT_LINE_EVENT: - cx_auto_line_automute(codec); - break; - case CONEXANT_MIC_EVENT: - cx_auto_automic(codec); - break; - } - snd_hda_jack_report_sync(codec); -} - /* check whether the pin config is suitable for auto-mic switching; * auto-mic is enabled only when one int-mic and one ext- and/or * one dock-mic exist @@ -3888,11 +3884,12 @@ static void mute_outputs(struct hda_codec *codec, int num_nids, } static void enable_unsol_pins(struct hda_codec *codec, int num_pins, - hda_nid_t *pins, unsigned int action) + hda_nid_t *pins, unsigned int action, + hda_jack_callback cb) { int i; for (i = 0; i < num_pins; i++) - snd_hda_jack_detect_enable(codec, pins[i], action); + snd_hda_jack_detect_enable_callback(codec, pins[i], action, cb); } static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) @@ -3980,13 +3977,14 @@ static void cx_auto_init_output(struct hda_codec *codec) } if (spec->auto_mute) { enable_unsol_pins(codec, cfg->hp_outs, cfg->hp_pins, - CONEXANT_HP_EVENT); + CONEXANT_HP_EVENT, cx_auto_hp_automute); spec->hp_present = detect_jacks(codec, cfg->hp_outs, cfg->hp_pins); if (spec->detect_line) { enable_unsol_pins(codec, cfg->line_outs, cfg->line_out_pins, - CONEXANT_LINE_EVENT); + CONEXANT_LINE_EVENT, + cx_auto_line_automute); spec->line_present = detect_jacks(codec, cfg->line_outs, cfg->line_out_pins); @@ -4027,16 +4025,16 @@ static void cx_auto_init_input(struct hda_codec *codec) if (spec->auto_mic) { if (spec->auto_mic_ext >= 0) { - snd_hda_jack_detect_enable(codec, + snd_hda_jack_detect_enable_callback(codec, cfg->inputs[spec->auto_mic_ext].pin, - CONEXANT_MIC_EVENT); + CONEXANT_MIC_EVENT, cx_auto_automic); } if (spec->auto_mic_dock >= 0) { - snd_hda_jack_detect_enable(codec, + snd_hda_jack_detect_enable_callback(codec, cfg->inputs[spec->auto_mic_dock].pin, - CONEXANT_MIC_EVENT); + CONEXANT_MIC_EVENT, cx_auto_automic); } - cx_auto_automic(codec); + cx_auto_automic(codec, NULL); } else { select_input_connection(codec, spec->imux_info[0].adc, spec->imux_info[0].pin); @@ -4061,7 +4059,6 @@ static int cx_auto_init(struct hda_codec *codec) cx_auto_init_output(codec); cx_auto_init_input(codec); cx_auto_init_digital(codec); - snd_hda_jack_report_sync(codec); snd_hda_sync_vmaster_hook(&spec->vmaster_mute); return 0; } @@ -4262,7 +4259,7 @@ static int cx_auto_add_boost_volume(struct hda_codec *codec, int idx, if (get_wcaps(codec, mux) & AC_WCAP_OUT_AMP) { spec->imux_info[idx].boost = mux; - return cx_auto_add_volume(codec, label, " Boost", 0, + return cx_auto_add_volume(codec, label, " Boost", cidx, mux, HDA_OUTPUT); } return 0; @@ -4395,8 +4392,8 @@ static const struct hda_codec_ops cx_auto_patch_ops = { .build_pcms = conexant_build_pcms, .init = cx_auto_init, .free = conexant_free, - .unsol_event = cx_auto_unsol_event, -#ifdef CONFIG_SND_HDA_POWER_SAVE + .unsol_event = snd_hda_jack_unsol_event, +#ifdef CONFIG_PM .suspend = conexant_suspend, #endif .reboot_notify = snd_hda_shutup_pins, @@ -4462,6 +4459,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = { SND_PCI_QUIRK(0x17aa, 0x21ce, "Lenovo T420", CXT_PINCFG_LENOVO_TP410), SND_PCI_QUIRK(0x17aa, 0x21cf, "Lenovo T520", CXT_PINCFG_LENOVO_TP410), SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC), + SND_PCI_QUIRK(0x17aa, 0x3977, "Lenovo IdeaPad U310", CXT_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x17aa, 0x397b, "Lenovo S205", CXT_FIXUP_STEREO_DMIC), {} }; diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 8f23374..71555cc 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -34,6 +34,8 @@ #include <linux/module.h> #include <sound/core.h> #include <sound/jack.h> +#include <sound/asoundef.h> +#include <sound/tlv.h> #include "hda_codec.h" #include "hda_local.h" #include "hda_jack.h" @@ -71,6 +73,9 @@ struct hdmi_spec_per_pin { struct hdmi_eld sink_eld; struct delayed_work work; int repoll_count; + bool non_pcm; + bool chmap_set; /* channel-map override by ALSA API? */ + unsigned char chmap[8]; /* ALSA API channel-map */ }; struct hdmi_spec { @@ -80,6 +85,7 @@ struct hdmi_spec { int num_pins; struct hdmi_spec_per_pin pins[MAX_HDMI_PINS]; struct hda_pcm pcm_rec[MAX_HDMI_PINS]; + unsigned int channels_max; /* max over all cvts */ /* * Non-generic ATI/NVIDIA specific @@ -469,6 +475,17 @@ static void init_channel_allocations(void) } } +static int get_channel_allocation_order(int ca) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + if (channel_allocations[i].ca_index == ca) + break; + } + return i; +} + /* * The transformation takes two steps: * @@ -535,24 +552,36 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec, } -static void hdmi_setup_channel_mapping(struct hda_codec *codec, +static void hdmi_std_setup_channel_mapping(struct hda_codec *codec, hda_nid_t pin_nid, + bool non_pcm, int ca) { int i; int err; + int order; + int non_pcm_mapping[8]; + + order = get_channel_allocation_order(ca); if (hdmi_channel_mapping[ca][1] == 0) { - for (i = 0; i < channel_allocations[ca].channels; i++) + for (i = 0; i < channel_allocations[order].channels; i++) hdmi_channel_mapping[ca][i] = i | (i << 4); for (; i < 8; i++) hdmi_channel_mapping[ca][i] = 0xf | (i << 4); } + if (non_pcm) { + for (i = 0; i < channel_allocations[order].channels; i++) + non_pcm_mapping[i] = i | (i << 4); + for (; i < 8; i++) + non_pcm_mapping[i] = 0xf | (i << 4); + } + for (i = 0; i < 8; i++) { err = snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_CHAN_SLOT, - hdmi_channel_mapping[ca][i]); + non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i]); if (err) { snd_printdd(KERN_NOTICE "HDMI: channel mapping failed\n"); @@ -563,6 +592,136 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec, hdmi_debug_channel_mapping(codec, pin_nid); } +struct channel_map_table { + unsigned char map; /* ALSA API channel map position */ + unsigned char cea_slot; /* CEA slot value */ + int spk_mask; /* speaker position bit mask */ +}; + +static struct channel_map_table map_tables[] = { + { SNDRV_CHMAP_FL, 0x00, FL }, + { SNDRV_CHMAP_FR, 0x01, FR }, + { SNDRV_CHMAP_RL, 0x04, RL }, + { SNDRV_CHMAP_RR, 0x05, RR }, + { SNDRV_CHMAP_LFE, 0x02, LFE }, + { SNDRV_CHMAP_FC, 0x03, FC }, + { SNDRV_CHMAP_RLC, 0x06, RLC }, + { SNDRV_CHMAP_RRC, 0x07, RRC }, + {} /* terminator */ +}; + +/* from ALSA API channel position to speaker bit mask */ +static int to_spk_mask(unsigned char c) +{ + struct channel_map_table *t = map_tables; + for (; t->map; t++) { + if (t->map == c) + return t->spk_mask; + } + return 0; +} + +/* from ALSA API channel position to CEA slot */ +static int to_cea_slot(unsigned char c) +{ + struct channel_map_table *t = map_tables; + for (; t->map; t++) { + if (t->map == c) + return t->cea_slot; + } + return 0x0f; +} + +/* from CEA slot to ALSA API channel position */ +static int from_cea_slot(unsigned char c) +{ + struct channel_map_table *t = map_tables; + for (; t->map; t++) { + if (t->cea_slot == c) + return t->map; + } + return 0; +} + +/* from speaker bit mask to ALSA API channel position */ +static int spk_to_chmap(int spk) +{ + struct channel_map_table *t = map_tables; + for (; t->map; t++) { + if (t->spk_mask == spk) + return t->map; + } + return 0; +} + +/* get the CA index corresponding to the given ALSA API channel map */ +static int hdmi_manual_channel_allocation(int chs, unsigned char *map) +{ + int i, spks = 0, spk_mask = 0; + + for (i = 0; i < chs; i++) { + int mask = to_spk_mask(map[i]); + if (mask) { + spk_mask |= mask; + spks++; + } + } + + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + if ((chs == channel_allocations[i].channels || + spks == channel_allocations[i].channels) && + (spk_mask & channel_allocations[i].spk_mask) == + channel_allocations[i].spk_mask) + return channel_allocations[i].ca_index; + } + return -1; +} + +/* set up the channel slots for the given ALSA API channel map */ +static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec, + hda_nid_t pin_nid, + int chs, unsigned char *map) +{ + int i; + for (i = 0; i < 8; i++) { + int val, err; + if (i < chs) + val = to_cea_slot(map[i]); + else + val = 0xf; + val |= (i << 4); + err = snd_hda_codec_write(codec, pin_nid, 0, + AC_VERB_SET_HDMI_CHAN_SLOT, val); + if (err) + return -EINVAL; + } + return 0; +} + +/* store ALSA API channel map from the current default map */ +static void hdmi_setup_fake_chmap(unsigned char *map, int ca) +{ + int i; + for (i = 0; i < 8; i++) { + if (i < channel_allocations[ca].channels) + map[i] = from_cea_slot((hdmi_channel_mapping[ca][i] >> 4) & 0x0f); + else + map[i] = 0; + } +} + +static void hdmi_setup_channel_mapping(struct hda_codec *codec, + hda_nid_t pin_nid, bool non_pcm, int ca, + int channels, unsigned char *map) +{ + if (!non_pcm && map) { + hdmi_manual_setup_channel_mapping(codec, pin_nid, + channels, map); + } else { + hdmi_std_setup_channel_mapping(codec, pin_nid, non_pcm, ca); + hdmi_setup_fake_chmap(map, ca); + } +} /* * Audio InfoFrame routines @@ -686,7 +845,8 @@ static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid, } static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx, - struct snd_pcm_substream *substream) + bool non_pcm, + struct snd_pcm_substream *substream) { struct hdmi_spec *spec = codec->spec; struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; @@ -700,7 +860,12 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx, if (!eld->monitor_present) return; - ca = hdmi_channel_allocation(eld, channels); + if (!non_pcm && per_pin->chmap_set) + ca = hdmi_manual_channel_allocation(channels, per_pin->chmap); + else + ca = hdmi_channel_allocation(eld, channels); + if (ca < 0) + ca = 0; memset(&ai, 0, sizeof(ai)); if (eld->conn_type == 0) { /* HDMI */ @@ -737,12 +902,21 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx, "pin=%d channels=%d\n", pin_nid, channels); - hdmi_setup_channel_mapping(codec, pin_nid, ca); + hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca, + channels, per_pin->chmap); hdmi_stop_infoframe_trans(codec, pin_nid); hdmi_fill_audio_infoframe(codec, pin_nid, ai.bytes, sizeof(ai)); hdmi_start_infoframe_trans(codec, pin_nid); + } else { + /* For non-pcm audio switch, setup new channel mapping + * accordingly */ + if (per_pin->non_pcm != non_pcm) + hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca, + channels, per_pin->chmap); } + + per_pin->non_pcm = non_pcm; } @@ -1035,6 +1209,7 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) per_pin = &spec->pins[pin_idx]; per_pin->pin_nid = pin_nid; + per_pin->non_pcm = false; err = hdmi_read_pin_conn(codec, pin_idx); if (err < 0) @@ -1064,8 +1239,11 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid) per_cvt->cvt_nid = cvt_nid; per_cvt->channels_min = 2; - if (chans <= 16) + if (chans <= 16) { per_cvt->channels_max = chans; + if (chans > spec->channels_max) + spec->channels_max = chans; + } err = snd_hda_query_supported_pcm(codec, cvt_nid, &per_cvt->rates, @@ -1115,7 +1293,7 @@ static int hdmi_parse_codec(struct hda_codec *codec) * can be lost and presence sense verb will become inaccurate if the * HDA link is powered off at hot plug or hw initialization time. */ -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM if (!(snd_hda_param_read(codec, codec->afg, AC_PAR_POWER_STATE) & AC_PWRST_EPSS)) codec->bus->power_keep_link_on = 1; @@ -1133,6 +1311,19 @@ static char *get_hdmi_pcm_name(int idx) return &names[idx][0]; } +static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid) +{ + struct hda_spdif_out *spdif; + bool non_pcm; + + mutex_lock(&codec->spdif_mutex); + spdif = snd_hda_spdif_out_of_nid(codec, cvt_nid); + non_pcm = !!(spdif->status & IEC958_AES0_NONAUDIO); + mutex_unlock(&codec->spdif_mutex); + return non_pcm; +} + + /* * HDMI callbacks */ @@ -1148,10 +1339,13 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, int pin_idx = hinfo_to_pin_index(spec, hinfo); hda_nid_t pin_nid = spec->pins[pin_idx].pin_nid; int pinctl; + bool non_pcm; + + non_pcm = check_non_pcm_per_cvt(codec, cvt_nid); hdmi_set_channel_count(codec, cvt_nid, substream->runtime->channels); - hdmi_setup_audio_infoframe(codec, pin_idx, substream); + hdmi_setup_audio_infoframe(codec, pin_idx, non_pcm, substream); pinctl = snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); @@ -1200,7 +1394,10 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl & ~PIN_OUT); snd_hda_spdif_ctls_unassign(codec, pin_idx); + per_pin->chmap_set = false; + memset(per_pin->chmap, 0, sizeof(per_pin->chmap)); } + return 0; } @@ -1211,6 +1408,135 @@ static const struct hda_pcm_ops generic_ops = { .cleanup = generic_hdmi_playback_pcm_cleanup, }; +/* + * ALSA API channel-map control callbacks + */ +static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + struct hda_codec *codec = info->private_data; + struct hdmi_spec *spec = codec->spec; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = spec->channels_max; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = SNDRV_CHMAP_LAST; + return 0; +} + +static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *tlv) +{ + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + struct hda_codec *codec = info->private_data; + struct hdmi_spec *spec = codec->spec; + const unsigned int valid_mask = + FL | FR | RL | RR | LFE | FC | RLC | RRC; + unsigned int __user *dst; + int chs, count = 0; + + if (size < 8) + return -ENOMEM; + if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv)) + return -EFAULT; + size -= 8; + dst = tlv + 2; + for (chs = 2; chs <= spec->channels_max; chs++) { + int i, c; + struct cea_channel_speaker_allocation *cap; + cap = channel_allocations; + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) { + int chs_bytes = chs * 4; + if (cap->channels != chs) + continue; + if (cap->spk_mask & ~valid_mask) + continue; + if (size < 8) + return -ENOMEM; + if (put_user(SNDRV_CTL_TLVT_CHMAP_VAR, dst) || + put_user(chs_bytes, dst + 1)) + return -EFAULT; + dst += 2; + size -= 8; + count += 8; + if (size < chs_bytes) + return -ENOMEM; + size -= chs_bytes; + count += chs_bytes; + for (c = 7; c >= 0; c--) { + int spk = cap->speakers[c]; + if (!spk) + continue; + if (put_user(spk_to_chmap(spk), dst)) + return -EFAULT; + dst++; + } + } + } + if (put_user(count, tlv + 1)) + return -EFAULT; + return 0; +} + +static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + struct hda_codec *codec = info->private_data; + struct hdmi_spec *spec = codec->spec; + int pin_idx = kcontrol->private_value; + struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; + int i; + + for (i = 0; i < ARRAY_SIZE(per_pin->chmap); i++) + ucontrol->value.integer.value[i] = per_pin->chmap[i]; + return 0; +} + +static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + struct hda_codec *codec = info->private_data; + struct hdmi_spec *spec = codec->spec; + int pin_idx = kcontrol->private_value; + struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; + unsigned int ctl_idx; + struct snd_pcm_substream *substream; + unsigned char chmap[8]; + int i, ca, prepared = 0; + + ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + substream = snd_pcm_chmap_substream(info, ctl_idx); + if (!substream || !substream->runtime) + return -EBADFD; + switch (substream->runtime->status->state) { + case SNDRV_PCM_STATE_OPEN: + case SNDRV_PCM_STATE_SETUP: + break; + case SNDRV_PCM_STATE_PREPARED: + prepared = 1; + break; + default: + return -EBUSY; + } + memset(chmap, 0, sizeof(chmap)); + for (i = 0; i < ARRAY_SIZE(chmap); i++) + chmap[i] = ucontrol->value.integer.value[i]; + if (!memcmp(chmap, per_pin->chmap, sizeof(chmap))) + return 0; + ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap); + if (ca < 0) + return -EINVAL; + per_pin->chmap_set = true; + memcpy(per_pin->chmap, chmap, sizeof(chmap)); + if (prepared) + hdmi_setup_audio_infoframe(codec, pin_idx, per_pin->non_pcm, + substream); + + return 0; +} + static int generic_hdmi_build_pcms(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; @@ -1223,6 +1549,7 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec) info = &spec->pcm_rec[pin_idx]; info->name = get_hdmi_pcm_name(pin_idx); info->pcm_type = HDA_PCM_TYPE_HDMI; + info->own_chmap = true; pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK]; pstr->substreams = 1; @@ -1280,6 +1607,27 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) hdmi_present_sense(per_pin, 0); } + /* add channel maps */ + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct snd_pcm_chmap *chmap; + struct snd_kcontrol *kctl; + int i; + err = snd_pcm_add_chmap_ctls(codec->pcm_info[pin_idx].pcm, + SNDRV_PCM_STREAM_PLAYBACK, + NULL, 0, pin_idx, &chmap); + if (err < 0) + return err; + /* override handlers */ + chmap->private_data = codec; + kctl = chmap->kctl; + for (i = 0; i < kctl->count; i++) + kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE; + kctl->info = hdmi_chmap_ctl_info; + kctl->get = hdmi_chmap_ctl_get; + kctl->put = hdmi_chmap_ctl_put; + kctl->tlv.c = hdmi_chmap_ctl_tlv; + } + return 0; } @@ -1311,7 +1659,6 @@ static int generic_hdmi_init(struct hda_codec *codec) hdmi_init_pin(codec, pin_nid); snd_hda_jack_detect_enable(codec, pin_nid, pin_nid); } - snd_hda_jack_report_sync(codec); return 0; } @@ -1428,7 +1775,6 @@ static int simple_playback_init(struct hda_codec *codec) snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); snd_hda_jack_detect_enable(codec, pin, pin); - snd_hda_jack_report_sync(codec); return 0; } @@ -1809,6 +2155,43 @@ static int patch_nvhdmi_2ch(struct hda_codec *codec) return 0; } +static int nvhdmi_7x_8ch_build_pcms(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + int err = simple_playback_build_pcms(codec); + spec->pcm_rec[0].own_chmap = true; + return err; +} + +static int nvhdmi_7x_8ch_build_controls(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + struct snd_pcm_chmap *chmap; + int err; + + err = simple_playback_build_controls(codec); + if (err < 0) + return err; + + /* add channel maps */ + err = snd_pcm_add_chmap_ctls(spec->pcm_rec[0].pcm, + SNDRV_PCM_STREAM_PLAYBACK, + snd_pcm_alt_chmaps, 8, 0, &chmap); + if (err < 0) + return err; + switch (codec->preset->id) { + case 0x10de0002: + case 0x10de0003: + case 0x10de0005: + case 0x10de0006: + chmap->channel_mask = (1U << 2) | (1U << 8); + break; + case 0x10de0007: + chmap->channel_mask = (1U << 2) | (1U << 6) | (1U << 8); + } + return 0; +} + static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) { struct hdmi_spec *spec; @@ -1819,6 +2202,8 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) spec->multiout.max_channels = 8; spec->pcm_playback = nvhdmi_pcm_playback_8ch_7x; codec->patch_ops.init = nvhdmi_7x_init_8ch; + codec->patch_ops.build_pcms = nvhdmi_7x_8ch_build_pcms; + codec->patch_ops.build_controls = nvhdmi_7x_8ch_build_controls; /* Initialize the audio infoframe channel mask and checksum to something * valid */ diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 4f81dd4..8568aee 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -174,7 +174,7 @@ struct alc_spec { /* hooks */ void (*init_hook)(struct hda_codec *codec); -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM void (*power_hook)(struct hda_codec *codec); #endif void (*shutup)(struct hda_codec *codec); @@ -215,7 +215,7 @@ struct alc_spec { /* for virtual master */ hda_nid_t vmaster_nid; struct hda_vmaster_mute_hook vmaster_mute; -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM struct hda_loopback_check loopback; int num_loopbacks; struct hda_amp_list loopback_list[8]; @@ -594,7 +594,7 @@ static void call_update_outputs(struct hda_codec *codec) } /* standard HP-automute helper */ -static void alc_hp_automute(struct hda_codec *codec) +static void alc_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) { struct alc_spec *spec = codec->spec; @@ -607,7 +607,7 @@ static void alc_hp_automute(struct hda_codec *codec) } /* standard line-out-automute helper */ -static void alc_line_automute(struct hda_codec *codec) +static void alc_line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) { struct alc_spec *spec = codec->spec; @@ -627,7 +627,7 @@ static void alc_line_automute(struct hda_codec *codec) snd_hda_get_conn_index(codec, mux, nid, 0) /* standard mic auto-switch helper */ -static void alc_mic_automute(struct hda_codec *codec) +static void alc_mic_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) { struct alc_spec *spec = codec->spec; hda_nid_t *pins = spec->imux_pins; @@ -648,25 +648,8 @@ static void alc_mic_automute(struct hda_codec *codec) alc_mux_select(codec, 0, spec->int_mic_idx, false); } -/* handle the specified unsol action (ALC_XXX_EVENT) */ -static void alc_exec_unsol_event(struct hda_codec *codec, int action) -{ - switch (action) { - case ALC_HP_EVENT: - alc_hp_automute(codec); - break; - case ALC_FRONT_EVENT: - alc_line_automute(codec); - break; - case ALC_MIC_EVENT: - alc_mic_automute(codec); - break; - } - snd_hda_jack_report_sync(codec); -} - /* update the master volume per volume-knob's unsol event */ -static void alc_update_knob_master(struct hda_codec *codec, hda_nid_t nid) +static void alc_update_knob_master(struct hda_codec *codec, struct hda_jack_tbl *jack) { unsigned int val; struct snd_kcontrol *kctl; @@ -678,7 +661,7 @@ static void alc_update_knob_master(struct hda_codec *codec, hda_nid_t nid) uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); if (!uctl) return; - val = snd_hda_codec_read(codec, nid, 0, + val = snd_hda_codec_read(codec, jack->nid, 0, AC_VERB_GET_VOLUME_KNOB_CONTROL, 0); val &= HDA_AMP_VOLMASK; uctl->value.integer.value[0] = val; @@ -687,37 +670,19 @@ static void alc_update_knob_master(struct hda_codec *codec, hda_nid_t nid) kfree(uctl); } -/* unsolicited event for HP jack sensing */ -static void alc_unsol_event(struct hda_codec *codec, unsigned int res) +static void alc880_unsol_event(struct hda_codec *codec, unsigned int res) { - int action; - - if (codec->vendor_id == 0x10ec0880) - res >>= 28; - else - res >>= 26; - action = snd_hda_jack_get_action(codec, res); - if (action == ALC_DCVOL_EVENT) { - /* Execute the dc-vol event here as it requires the NID - * but we don't pass NID to alc_exec_unsol_event(). - * Once when we convert all static quirks to the auto-parser, - * this can be integerated into there. - */ - struct hda_jack_tbl *jack; - jack = snd_hda_jack_tbl_get_from_tag(codec, res); - if (jack) - alc_update_knob_master(codec, jack->nid); - return; - } - alc_exec_unsol_event(codec, action); + /* For some reason, the res given from ALC880 is broken. + Here we adjust it properly. */ + snd_hda_jack_unsol_event(codec, res >> 2); } /* call init functions of standard auto-mute helpers */ static void alc_inithook(struct hda_codec *codec) { - alc_hp_automute(codec); - alc_line_automute(codec); - alc_mic_automute(codec); + alc_hp_automute(codec, NULL); + alc_line_automute(codec, NULL); + alc_mic_automute(codec, NULL); } /* additional initialization for ALC888 variants */ @@ -999,7 +964,8 @@ static void alc_init_automute(struct hda_codec *codec) continue; snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n", nid); - snd_hda_jack_detect_enable(codec, nid, ALC_HP_EVENT); + snd_hda_jack_detect_enable_callback(codec, nid, ALC_HP_EVENT, + alc_hp_automute); spec->detect_hp = 1; } @@ -1011,10 +977,10 @@ static void alc_init_automute(struct hda_codec *codec) continue; snd_printdd("realtek: Enable Line-Out " "auto-muting on NID 0x%x\n", nid); - snd_hda_jack_detect_enable(codec, nid, - ALC_FRONT_EVENT); + snd_hda_jack_detect_enable_callback(codec, nid, ALC_FRONT_EVENT, + alc_line_automute); spec->detect_lo = 1; - } + } spec->automute_lo_possible = spec->detect_hp; } @@ -1110,10 +1076,12 @@ static bool alc_auto_mic_check_imux(struct hda_codec *codec) return false; /* no corresponding imux */ } - snd_hda_jack_detect_enable(codec, spec->ext_mic_pin, ALC_MIC_EVENT); + snd_hda_jack_detect_enable_callback(codec, spec->ext_mic_pin, + ALC_MIC_EVENT, alc_mic_automute); if (spec->dock_mic_pin) - snd_hda_jack_detect_enable(codec, spec->dock_mic_pin, - ALC_MIC_EVENT); + snd_hda_jack_detect_enable_callback(codec, spec->dock_mic_pin, + ALC_MIC_EVENT, + alc_mic_automute); spec->auto_mic_valid_imux = 1; spec->auto_mic = 1; @@ -2053,13 +2021,11 @@ static int alc_init(struct hda_codec *codec) alc_apply_fixup(codec, ALC_FIXUP_ACT_INIT); - snd_hda_jack_report_sync(codec); - hda_call_check_power_status(codec, 0x01); return 0; } -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM static int alc_check_power_status(struct hda_codec *codec, hda_nid_t nid) { struct alc_spec *spec = codec->spec; @@ -2289,6 +2255,8 @@ static int alc_build_pcms(struct hda_codec *codec) p = &alc_pcm_analog_playback; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = + spec->multiout.max_channels; } if (spec->adc_nids) { p = spec->stream_analog_capture; @@ -2437,7 +2405,7 @@ static void alc_free(struct hda_codec *codec) snd_hda_detach_beep_device(codec); } -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM static void alc_power_eapd(struct hda_codec *codec) { alc_auto_setup_eapd(codec, false); @@ -2473,17 +2441,18 @@ static const struct hda_codec_ops alc_patch_ops = { .build_pcms = alc_build_pcms, .init = alc_init, .free = alc_free, - .unsol_event = alc_unsol_event, + .unsol_event = snd_hda_jack_unsol_event, #ifdef CONFIG_PM .resume = alc_resume, #endif -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM .suspend = alc_suspend, .check_power_status = alc_check_power_status, #endif .reboot_notify = alc_shutup, }; + /* replace the codec chip_name with the given string */ static int alc_codec_rename(struct hda_codec *codec, const char *name) { @@ -2633,7 +2602,7 @@ static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch, return channel_name[ch]; } -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM /* add the powersave loopback-list entry */ static void add_loopback_list(struct alc_spec *spec, hda_nid_t mix, int idx) { @@ -4447,7 +4416,7 @@ static void alc880_fixup_vol_knob(struct hda_codec *codec, const struct alc_fixup *fix, int action) { if (action == ALC_FIXUP_ACT_PROBE) - snd_hda_jack_detect_enable(codec, 0x21, ALC_DCVOL_EVENT); + snd_hda_jack_detect_enable_callback(codec, 0x21, ALC_DCVOL_EVENT, alc_update_knob_master); } static const struct alc_fixup alc880_fixups[] = { @@ -4812,6 +4781,8 @@ static int patch_alc880(struct hda_codec *codec) } codec->patch_ops = alc_patch_ops; + codec->patch_ops.unsol_event = alc880_unsol_event; + alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); @@ -4866,7 +4837,8 @@ static void alc260_fixup_gpio1_toggle(struct hda_codec *codec, spec->detect_hp = 1; spec->automute_speaker = 1; spec->autocfg.hp_pins[0] = 0x0f; /* copy it for automute */ - snd_hda_jack_detect_enable(codec, 0x0f, ALC_HP_EVENT); + snd_hda_jack_detect_enable_callback(codec, 0x0f, ALC_HP_EVENT, + alc_hp_automute); snd_hda_gen_add_verbs(&spec->gen, alc_gpio1_init_verbs); } } @@ -6189,6 +6161,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x0349, "Acer AOD260", ALC269_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_MIC2_MUTE_LED), SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_DMIC), + SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_DMIC), SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW), SND_PCI_QUIRK(0x1043, 0x1b13, "Asus U41SV", ALC269_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC), @@ -6334,6 +6307,12 @@ static int patch_alc269(struct hda_codec *codec) spec = codec->spec; + alc_pick_fixup(codec, alc269_fixup_models, + alc269_fixup_tbl, alc269_fixups); + alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + + alc_auto_parse_customize_define(codec); + if (codec->vendor_id == 0x10ec0269) { spec->codec_variant = ALC269_TYPE_ALC269VA; switch (alc_get_coef0(codec) & 0x00f0) { @@ -6361,12 +6340,6 @@ static int patch_alc269(struct hda_codec *codec) alc269_fill_coef(codec); } - alc_pick_fixup(codec, alc269_fixup_models, - alc269_fixup_tbl, alc269_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); - - alc_auto_parse_customize_define(codec); - /* automatic parse from the BIOS config */ err = alc269_parse_auto_config(codec); if (err < 0) @@ -6503,7 +6476,7 @@ static int patch_alc861(struct hda_codec *codec) } codec->patch_ops = alc_patch_ops; -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM spec->power_hook = alc_power_eapd; #endif @@ -7068,6 +7041,8 @@ static const struct hda_codec_preset snd_hda_preset_realtek[] = { { .id = 0x10ec0276, .name = "ALC276", .patch = patch_alc269 }, { .id = 0x10ec0280, .name = "ALC280", .patch = patch_alc269 }, { .id = 0x10ec0282, .name = "ALC282", .patch = patch_alc269 }, + { .id = 0x10ec0283, .name = "ALC283", .patch = patch_alc269 }, + { .id = 0x10ec0290, .name = "ALC290", .patch = patch_alc269 }, { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660", .patch = patch_alc861 }, { .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd }, diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 3d4722f..fe16354 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -103,6 +103,7 @@ enum { STAC_HP_ZEPHYR, STAC_92HD83XXX_HP_LED, STAC_92HD83XXX_HP_INV_LED, + STAC_92HD83XXX_HP_MIC_LED, STAC_92HD83XXX_MODELS }; @@ -215,6 +216,9 @@ struct sigmatel_spec { unsigned int vref_mute_led_nid; /* pin NID for mute-LED vref control */ unsigned int vref_led; + unsigned int mic_mute_led_gpio; /* capture mute LED GPIO */ + bool mic_mute_led_on; /* current mic mute state */ + /* stream */ unsigned int stream_delay; @@ -1679,6 +1683,7 @@ static const char * const stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = { [STAC_HP_ZEPHYR] = "hp-zephyr", [STAC_92HD83XXX_HP_LED] = "hp-led", [STAC_92HD83XXX_HP_INV_LED] = "hp-inv-led", + [STAC_92HD83XXX_HP_MIC_LED] = "hp-mic-led", }; static const struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = { @@ -1703,6 +1708,8 @@ static const struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = { "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x165B, "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x18df, + "HP Folio", STAC_92HD83XXX_HP_MIC_LED), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3388, "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3389, @@ -2791,18 +2798,27 @@ stac_control_new(struct sigmatel_spec *spec, return knew; } -static int stac92xx_add_control_temp(struct sigmatel_spec *spec, - const struct snd_kcontrol_new *ktemp, - int idx, const char *name, - unsigned long val) +static struct snd_kcontrol_new * +add_control_temp(struct sigmatel_spec *spec, + const struct snd_kcontrol_new *ktemp, + int idx, const char *name, + unsigned long val) { struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name, HDA_SUBDEV_AMP_FLAG); if (!knew) - return -ENOMEM; + return NULL; knew->index = idx; knew->private_value = val; - return 0; + return knew; +} + +static int stac92xx_add_control_temp(struct sigmatel_spec *spec, + const struct snd_kcontrol_new *ktemp, + int idx, const char *name, + unsigned long val) +{ + return add_control_temp(spec, ktemp, idx, name, val) ? 0 : -ENOMEM; } static inline int stac92xx_add_control_idx(struct sigmatel_spec *spec, @@ -3226,9 +3242,12 @@ static int create_multi_out_ctls(struct hda_codec *codec, int num_outs, idx = i; break; case AUTO_PIN_SPEAKER_OUT: - name = "Speaker"; - idx = i; - break; + if (num_outs <= 1) { + name = "Speaker"; + idx = i; + break; + } + /* Fall through in case of multi speaker outs */ default: name = chname[i]; idx = 0; @@ -3242,18 +3261,56 @@ static int create_multi_out_ctls(struct hda_codec *codec, int num_outs, return 0; } +static void stac_gpio_set(struct hda_codec *codec, unsigned int mask, + unsigned int dir_mask, unsigned int data); + +/* hook for controlling mic-mute LED GPIO */ +static int stac92xx_capture_sw_put_led(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + int err; + bool mute; + + err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); + if (err <= 0) + return err; + mute = !(ucontrol->value.integer.value[0] && + ucontrol->value.integer.value[1]); + if (spec->mic_mute_led_on != mute) { + spec->mic_mute_led_on = mute; + if (mute) + spec->gpio_data |= spec->mic_mute_led_gpio; + else + spec->gpio_data &= ~spec->mic_mute_led_gpio; + stac_gpio_set(codec, spec->gpio_mask, + spec->gpio_dir, spec->gpio_data); + } + return err; +} + static int stac92xx_add_capvol_ctls(struct hda_codec *codec, unsigned long vol, unsigned long sw, int idx) { + struct sigmatel_spec *spec = codec->spec; + struct snd_kcontrol_new *knew; int err; + err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx, "Capture Volume", vol); if (err < 0) return err; - err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_MUTE, idx, - "Capture Switch", sw); - if (err < 0) - return err; + + knew = add_control_temp(spec, + &stac92xx_control_templates[STAC_CTL_WIDGET_MUTE], + idx, "Capture Switch", sw); + if (!knew) + return -ENOMEM; + /* add a LED hook for some HP laptops */ + if (spec->mic_mute_led_gpio) + knew->put = stac92xx_capture_sw_put_led; + return 0; } @@ -4155,6 +4212,9 @@ static int stac_add_event(struct hda_codec *codec, hda_nid_t nid, return 0; } +static void handle_unsol_event(struct hda_codec *codec, + struct hda_jack_tbl *event); + /* check if given nid is a valid pin and no other events are assigned * to it. If OK, assign the event, set the unsol flag, and returns 1. * Otherwise, returns zero. @@ -4172,6 +4232,7 @@ static int enable_pin_detect(struct hda_codec *codec, hda_nid_t nid, if (event->action && event->action != type) return 0; event->action = type; + event->callback = handle_unsol_event; snd_hda_jack_detect_enable(codec, nid, 0); return 1; } @@ -4418,8 +4479,6 @@ static int stac92xx_init(struct hda_codec *codec) stac_toggle_power_map(codec, nid, 0); } - snd_hda_jack_report_sync(codec); - /* sync mute LED */ if (spec->gpio_led) { if (spec->vmaster_mute.hook) @@ -4812,20 +4871,6 @@ static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid) handle_unsol_event(codec, event); } -static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res) -{ - struct hda_jack_tbl *event; - int tag; - - tag = (res >> 26) & 0x7f; - event = snd_hda_jack_tbl_get_from_tag(codec, tag); - if (!event) - return; - event->jack_dirty = 1; - handle_unsol_event(codec, event); - snd_hda_jack_report_sync(codec); -} - static int hp_blike_system(u32 subsystem_id); static void set_hp_led_gpio(struct hda_codec *codec) @@ -5076,7 +5121,7 @@ static const struct hda_codec_ops stac92xx_patch_ops = { .build_pcms = stac92xx_build_pcms, .init = stac92xx_init, .free = stac92xx_free, - .unsol_event = stac92xx_unsol_event, + .unsol_event = snd_hda_jack_unsol_event, #ifdef CONFIG_PM .suspend = stac92xx_suspend, .resume = stac92xx_resume, @@ -5578,6 +5623,9 @@ again: case STAC_92HD83XXX_HP_INV_LED: default_polarity = 1; break; + case STAC_92HD83XXX_HP_MIC_LED: + spec->mic_mute_led_gpio = 0x08; /* GPIO3 */ + break; } if (find_mute_led_cfg(codec, default_polarity)) @@ -5596,6 +5644,13 @@ again: } } + if (spec->mic_mute_led_gpio) { + spec->gpio_mask |= spec->mic_mute_led_gpio; + spec->gpio_dir |= spec->mic_mute_led_gpio; + spec->mic_mute_led_on = true; + spec->gpio_data |= spec->mic_mute_led_gpio; + } + err = stac92xx_parse_auto_config(codec); if (!err) { if (spec->board_config < 0) { diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 4307717..5a45a91 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1672,7 +1672,8 @@ static void via_hp_automute(struct hda_codec *codec) struct via_spec *spec = codec->spec; if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0] && - (spec->codec_type != VT1708 || spec->vt1708_jack_detect)) + (spec->codec_type != VT1708 || spec->vt1708_jack_detect) && + is_jack_detectable(codec, spec->autocfg.hp_pins[0])) present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); if (spec->smart51_enabled) @@ -1764,7 +1765,7 @@ static int via_suspend(struct hda_codec *codec) } #endif -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) { struct via_spec *spec = codec->spec; @@ -1785,8 +1786,6 @@ static const struct hda_codec_ops via_patch_ops = { .unsol_event = via_unsol_event, #ifdef CONFIG_PM .suspend = via_suspend, -#endif -#ifdef CONFIG_SND_HDA_POWER_SAVE .check_power_status = via_check_power_status, #endif }; @@ -2815,7 +2814,6 @@ static int via_init(struct hda_codec *codec) via_hp_automute(codec); vt1708_update_hp_work(spec); - snd_hda_jack_report_sync(codec); return 0; } @@ -3669,6 +3667,32 @@ static void set_widgets_power_state_vt2002P(struct hda_codec *codec) update_power_state(codec, 0x21, AC_PWRST_D3); } +/* + * pin fix-up + */ +enum { + VIA_FIXUP_INTMIC_BOOST, +}; + +static void via_fixup_intmic_boost(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) + override_mic_boost(codec, 0x30, 0, 2, 40); +} + +static const struct hda_fixup via_fixups[] = { + [VIA_FIXUP_INTMIC_BOOST] = { + .type = HDA_FIXUP_FUNC, + .v.func = via_fixup_intmic_boost, + }, +}; + +static const struct snd_pci_quirk vt2002p_fixups[] = { + SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST), + {} +}; + /* patch for vt2002P */ static int patch_vt2002P(struct hda_codec *codec) { @@ -3685,6 +3709,9 @@ static int patch_vt2002P(struct hda_codec *codec) override_mic_boost(codec, 0x29, 0, 3, 40); add_secret_dac_path(codec); + snd_hda_pick_fixup(codec, NULL, vt2002p_fixups, via_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + /* automatic parse from the BIOS config */ err = via_parse_auto_config(codec); if (err < 0) { |