diff options
Diffstat (limited to 'sound/pci/hda/hda_codec.c')
-rw-r--r-- | sound/pci/hda/hda_codec.c | 276 |
1 files changed, 163 insertions, 113 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 1c65cc5e3a3..c0ab72cbeed 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) { |