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>
This commit is contained in:
Kailang Yang 2013-06-28 12:03:01 +02:00 коммит произвёл Takashi Iwai
Родитель db10e7fbbc
Коммит ad60d502fb
1 изменённых файлов: 98 добавлений и 0 удалений

Просмотреть файл

@ -115,6 +115,7 @@ struct alc_spec {
int init_amp; int init_amp;
int codec_variant; /* flag for other variants */ int codec_variant; /* flag for other variants */
bool has_alc5505_dsp;
/* for PLL fix */ /* for PLL fix */
hda_nid_t pll_nid; hda_nid_t pll_nid;
@ -2580,7 +2581,96 @@ static void alc269_shutup(struct hda_codec *codec)
} }
} }
static void alc5505_coef_set(struct hda_codec *codec, unsigned int index_reg,
unsigned int val)
{
snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_COEF_INDEX, index_reg >> 1);
snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_PROC_COEF, val & 0xffff); /* LSB */
snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_PROC_COEF, val >> 16); /* MSB */
}
static int alc5505_coef_get(struct hda_codec *codec, unsigned int index_reg)
{
unsigned int val;
snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_COEF_INDEX, index_reg >> 1);
val = snd_hda_codec_read(codec, 0x51, 0, AC_VERB_GET_PROC_COEF, 0)
& 0xffff;
val |= snd_hda_codec_read(codec, 0x51, 0, AC_VERB_GET_PROC_COEF, 0)
<< 16;
return val;
}
static void alc5505_dsp_halt(struct hda_codec *codec)
{
unsigned int val;
alc5505_coef_set(codec, 0x3000, 0x000c); /* DSP CPU stop */
alc5505_coef_set(codec, 0x880c, 0x0008); /* DDR enter self refresh */
alc5505_coef_set(codec, 0x61c0, 0x11110080); /* Clock control for PLL and CPU */
alc5505_coef_set(codec, 0x6230, 0xfc0d4011); /* Disable Input OP */
alc5505_coef_set(codec, 0x61b4, 0x040a2b03); /* Stop PLL2 */
alc5505_coef_set(codec, 0x61b0, 0x00005b17); /* Stop PLL1 */
alc5505_coef_set(codec, 0x61b8, 0x04133303); /* Stop PLL3 */
val = alc5505_coef_get(codec, 0x6220);
alc5505_coef_set(codec, 0x6220, (val | 0x3000)); /* switch Ringbuffer clock to DBUS clock */
}
static void alc5505_dsp_back_from_halt(struct hda_codec *codec)
{
alc5505_coef_set(codec, 0x61b8, 0x04133302);
alc5505_coef_set(codec, 0x61b0, 0x00005b16);
alc5505_coef_set(codec, 0x61b4, 0x040a2b02);
alc5505_coef_set(codec, 0x6230, 0xf80d4011);
alc5505_coef_set(codec, 0x6220, 0x2002010f);
alc5505_coef_set(codec, 0x880c, 0x00000004);
}
static void alc5505_dsp_init(struct hda_codec *codec)
{
unsigned int val;
alc5505_dsp_halt(codec);
alc5505_dsp_back_from_halt(codec);
alc5505_coef_set(codec, 0x61b0, 0x5b14); /* PLL1 control */
alc5505_coef_set(codec, 0x61b0, 0x5b16);
alc5505_coef_set(codec, 0x61b4, 0x04132b00); /* PLL2 control */
alc5505_coef_set(codec, 0x61b4, 0x04132b02);
alc5505_coef_set(codec, 0x61b8, 0x041f3300); /* PLL3 control*/
alc5505_coef_set(codec, 0x61b8, 0x041f3302);
snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_CODEC_RESET, 0); /* Function reset */
alc5505_coef_set(codec, 0x61b8, 0x041b3302);
alc5505_coef_set(codec, 0x61b8, 0x04173302);
alc5505_coef_set(codec, 0x61b8, 0x04163302);
alc5505_coef_set(codec, 0x8800, 0x348b328b); /* DRAM control */
alc5505_coef_set(codec, 0x8808, 0x00020022); /* DRAM control */
alc5505_coef_set(codec, 0x8818, 0x00000400); /* DRAM control */
val = alc5505_coef_get(codec, 0x6200) >> 16; /* Read revision ID */
if (val <= 3)
alc5505_coef_set(codec, 0x6220, 0x2002010f); /* I/O PAD Configuration */
else
alc5505_coef_set(codec, 0x6220, 0x6002018f);
alc5505_coef_set(codec, 0x61ac, 0x055525f0); /**/
alc5505_coef_set(codec, 0x61c0, 0x12230080); /* Clock control */
alc5505_coef_set(codec, 0x61b4, 0x040e2b02); /* PLL2 control */
alc5505_coef_set(codec, 0x61bc, 0x010234f8); /* OSC Control */
alc5505_coef_set(codec, 0x880c, 0x00000004); /* DRAM Function control */
alc5505_coef_set(codec, 0x880c, 0x00000003);
alc5505_coef_set(codec, 0x880c, 0x00000010);
}
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int alc269_suspend(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
if (spec->has_alc5505_dsp)
alc5505_dsp_halt(codec);
return alc_suspend(codec);
}
static int alc269_resume(struct hda_codec *codec) static int alc269_resume(struct hda_codec *codec)
{ {
struct alc_spec *spec = codec->spec; struct alc_spec *spec = codec->spec;
@ -2605,6 +2695,8 @@ static int alc269_resume(struct hda_codec *codec)
snd_hda_codec_resume_cache(codec); snd_hda_codec_resume_cache(codec);
alc_inv_dmic_sync(codec, true); alc_inv_dmic_sync(codec, true);
hda_call_check_power_status(codec, 0x01); hda_call_check_power_status(codec, 0x01);
if (spec->has_alc5505_dsp)
alc5505_dsp_back_from_halt(codec);
return 0; return 0;
} }
#endif /* CONFIG_PM */ #endif /* CONFIG_PM */
@ -3730,6 +3822,11 @@ static int patch_alc269(struct hda_codec *codec)
break; break;
} }
if (snd_hda_codec_read(codec, 0x51, 0, AC_VERB_PARAMETERS, 0) == 0x10ec5505) {
spec->has_alc5505_dsp = true;
spec->init_hook = alc5505_dsp_init;
}
/* automatic parse from the BIOS config */ /* automatic parse from the BIOS config */
err = alc269_parse_auto_config(codec); err = alc269_parse_auto_config(codec);
if (err < 0) if (err < 0)
@ -3740,6 +3837,7 @@ static int patch_alc269(struct hda_codec *codec)
codec->patch_ops = alc_patch_ops; codec->patch_ops = alc_patch_ops;
#ifdef CONFIG_PM #ifdef CONFIG_PM
codec->patch_ops.suspend = alc269_suspend;
codec->patch_ops.resume = alc269_resume; codec->patch_ops.resume = alc269_resume;
#endif #endif
spec->shutup = alc269_shutup; spec->shutup = alc269_shutup;