diff options
author | Kailang Yang <kailang@realtek.com> | 2013-06-28 06:03:01 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2013-06-28 06:12:54 -0400 |
commit | ad60d502fb8aaa3c1e011f4d72b8228f553d87a8 (patch) | |
tree | 5fdd0ec7256d52e60f73ff831bf2796f830b408d /sound/pci/hda | |
parent | db10e7fbbc836fb66d4500c64c1960940cfad2b0 (diff) |
ALSA: hda - Add support for ALC5505 DSP power-save mode
This patch adds the power-saving control for ALC5505 DSP on some
Realtek codecs.
Signed-off-by: Kailang Yang <kailang@realtek.com>
Tested-by: Mengdong Lin <mengdong.lin@intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda')
-rw-r--r-- | sound/pci/hda/patch_realtek.c | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index ae121113f223..eeb6ecc31366 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c | |||
@@ -115,6 +115,7 @@ struct alc_spec { | |||
115 | 115 | ||
116 | int init_amp; | 116 | int init_amp; |
117 | int codec_variant; /* flag for other variants */ | 117 | int codec_variant; /* flag for other variants */ |
118 | bool has_alc5505_dsp; | ||
118 | 119 | ||
119 | /* for PLL fix */ | 120 | /* for PLL fix */ |
120 | hda_nid_t pll_nid; | 121 | hda_nid_t pll_nid; |
@@ -2580,7 +2581,96 @@ static void alc269_shutup(struct hda_codec *codec) | |||
2580 | } | 2581 | } |
2581 | } | 2582 | } |
2582 | 2583 | ||
2584 | static void alc5505_coef_set(struct hda_codec *codec, unsigned int index_reg, | ||
2585 | unsigned int val) | ||
2586 | { | ||
2587 | snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_COEF_INDEX, index_reg >> 1); | ||
2588 | snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_PROC_COEF, val & 0xffff); /* LSB */ | ||
2589 | snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_PROC_COEF, val >> 16); /* MSB */ | ||
2590 | } | ||
2591 | |||
2592 | static int alc5505_coef_get(struct hda_codec *codec, unsigned int index_reg) | ||
2593 | { | ||
2594 | unsigned int val; | ||
2595 | |||
2596 | snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_COEF_INDEX, index_reg >> 1); | ||
2597 | val = snd_hda_codec_read(codec, 0x51, 0, AC_VERB_GET_PROC_COEF, 0) | ||
2598 | & 0xffff; | ||
2599 | val |= snd_hda_codec_read(codec, 0x51, 0, AC_VERB_GET_PROC_COEF, 0) | ||
2600 | << 16; | ||
2601 | return val; | ||
2602 | } | ||
2603 | |||
2604 | static void alc5505_dsp_halt(struct hda_codec *codec) | ||
2605 | { | ||
2606 | unsigned int val; | ||
2607 | |||
2608 | alc5505_coef_set(codec, 0x3000, 0x000c); /* DSP CPU stop */ | ||
2609 | alc5505_coef_set(codec, 0x880c, 0x0008); /* DDR enter self refresh */ | ||
2610 | alc5505_coef_set(codec, 0x61c0, 0x11110080); /* Clock control for PLL and CPU */ | ||
2611 | alc5505_coef_set(codec, 0x6230, 0xfc0d4011); /* Disable Input OP */ | ||
2612 | alc5505_coef_set(codec, 0x61b4, 0x040a2b03); /* Stop PLL2 */ | ||
2613 | alc5505_coef_set(codec, 0x61b0, 0x00005b17); /* Stop PLL1 */ | ||
2614 | alc5505_coef_set(codec, 0x61b8, 0x04133303); /* Stop PLL3 */ | ||
2615 | val = alc5505_coef_get(codec, 0x6220); | ||
2616 | alc5505_coef_set(codec, 0x6220, (val | 0x3000)); /* switch Ringbuffer clock to DBUS clock */ | ||
2617 | } | ||
2618 | |||
2619 | static void alc5505_dsp_back_from_halt(struct hda_codec *codec) | ||
2620 | { | ||
2621 | alc5505_coef_set(codec, 0x61b8, 0x04133302); | ||
2622 | alc5505_coef_set(codec, 0x61b0, 0x00005b16); | ||
2623 | alc5505_coef_set(codec, 0x61b4, 0x040a2b02); | ||
2624 | alc5505_coef_set(codec, 0x6230, 0xf80d4011); | ||
2625 | alc5505_coef_set(codec, 0x6220, 0x2002010f); | ||
2626 | alc5505_coef_set(codec, 0x880c, 0x00000004); | ||
2627 | } | ||
2628 | |||
2629 | static void alc5505_dsp_init(struct hda_codec *codec) | ||
2630 | { | ||
2631 | unsigned int val; | ||
2632 | |||
2633 | alc5505_dsp_halt(codec); | ||
2634 | alc5505_dsp_back_from_halt(codec); | ||
2635 | alc5505_coef_set(codec, 0x61b0, 0x5b14); /* PLL1 control */ | ||
2636 | alc5505_coef_set(codec, 0x61b0, 0x5b16); | ||
2637 | alc5505_coef_set(codec, 0x61b4, 0x04132b00); /* PLL2 control */ | ||
2638 | alc5505_coef_set(codec, 0x61b4, 0x04132b02); | ||
2639 | alc5505_coef_set(codec, 0x61b8, 0x041f3300); /* PLL3 control*/ | ||
2640 | alc5505_coef_set(codec, 0x61b8, 0x041f3302); | ||
2641 | snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_CODEC_RESET, 0); /* Function reset */ | ||
2642 | alc5505_coef_set(codec, 0x61b8, 0x041b3302); | ||
2643 | alc5505_coef_set(codec, 0x61b8, 0x04173302); | ||
2644 | alc5505_coef_set(codec, 0x61b8, 0x04163302); | ||
2645 | alc5505_coef_set(codec, 0x8800, 0x348b328b); /* DRAM control */ | ||
2646 | alc5505_coef_set(codec, 0x8808, 0x00020022); /* DRAM control */ | ||
2647 | alc5505_coef_set(codec, 0x8818, 0x00000400); /* DRAM control */ | ||
2648 | |||
2649 | val = alc5505_coef_get(codec, 0x6200) >> 16; /* Read revision ID */ | ||
2650 | if (val <= 3) | ||
2651 | alc5505_coef_set(codec, 0x6220, 0x2002010f); /* I/O PAD Configuration */ | ||
2652 | else | ||
2653 | alc5505_coef_set(codec, 0x6220, 0x6002018f); | ||
2654 | |||
2655 | alc5505_coef_set(codec, 0x61ac, 0x055525f0); /**/ | ||
2656 | alc5505_coef_set(codec, 0x61c0, 0x12230080); /* Clock control */ | ||
2657 | alc5505_coef_set(codec, 0x61b4, 0x040e2b02); /* PLL2 control */ | ||
2658 | alc5505_coef_set(codec, 0x61bc, 0x010234f8); /* OSC Control */ | ||
2659 | alc5505_coef_set(codec, 0x880c, 0x00000004); /* DRAM Function control */ | ||
2660 | alc5505_coef_set(codec, 0x880c, 0x00000003); | ||
2661 | alc5505_coef_set(codec, 0x880c, 0x00000010); | ||
2662 | } | ||
2663 | |||
2583 | #ifdef CONFIG_PM | 2664 | #ifdef CONFIG_PM |
2665 | static int alc269_suspend(struct hda_codec *codec) | ||
2666 | { | ||
2667 | struct alc_spec *spec = codec->spec; | ||
2668 | |||
2669 | if (spec->has_alc5505_dsp) | ||
2670 | alc5505_dsp_halt(codec); | ||
2671 | return alc_suspend(codec); | ||
2672 | } | ||
2673 | |||
2584 | static int alc269_resume(struct hda_codec *codec) | 2674 | static int alc269_resume(struct hda_codec *codec) |
2585 | { | 2675 | { |
2586 | struct alc_spec *spec = codec->spec; | 2676 | struct alc_spec *spec = codec->spec; |
@@ -2605,6 +2695,8 @@ static int alc269_resume(struct hda_codec *codec) | |||
2605 | snd_hda_codec_resume_cache(codec); | 2695 | snd_hda_codec_resume_cache(codec); |
2606 | alc_inv_dmic_sync(codec, true); | 2696 | alc_inv_dmic_sync(codec, true); |
2607 | hda_call_check_power_status(codec, 0x01); | 2697 | hda_call_check_power_status(codec, 0x01); |
2698 | if (spec->has_alc5505_dsp) | ||
2699 | alc5505_dsp_back_from_halt(codec); | ||
2608 | return 0; | 2700 | return 0; |
2609 | } | 2701 | } |
2610 | #endif /* CONFIG_PM */ | 2702 | #endif /* CONFIG_PM */ |
@@ -3730,6 +3822,11 @@ static int patch_alc269(struct hda_codec *codec) | |||
3730 | break; | 3822 | break; |
3731 | } | 3823 | } |
3732 | 3824 | ||
3825 | if (snd_hda_codec_read(codec, 0x51, 0, AC_VERB_PARAMETERS, 0) == 0x10ec5505) { | ||
3826 | spec->has_alc5505_dsp = true; | ||
3827 | spec->init_hook = alc5505_dsp_init; | ||
3828 | } | ||
3829 | |||
3733 | /* automatic parse from the BIOS config */ | 3830 | /* automatic parse from the BIOS config */ |
3734 | err = alc269_parse_auto_config(codec); | 3831 | err = alc269_parse_auto_config(codec); |
3735 | if (err < 0) | 3832 | if (err < 0) |
@@ -3740,6 +3837,7 @@ static int patch_alc269(struct hda_codec *codec) | |||
3740 | 3837 | ||
3741 | codec->patch_ops = alc_patch_ops; | 3838 | codec->patch_ops = alc_patch_ops; |
3742 | #ifdef CONFIG_PM | 3839 | #ifdef CONFIG_PM |
3840 | codec->patch_ops.suspend = alc269_suspend; | ||
3743 | codec->patch_ops.resume = alc269_resume; | 3841 | codec->patch_ops.resume = alc269_resume; |
3744 | #endif | 3842 | #endif |
3745 | spec->shutup = alc269_shutup; | 3843 | spec->shutup = alc269_shutup; |