diff options
| author | Libin Yang <libin.yang@intel.com> | 2017-04-06 14:18:21 +0300 | 
|---|---|---|
| committer | Takashi Iwai <tiwai@suse.de> | 2017-04-07 11:39:21 +0300 | 
| commit | 1f9d3d98694b1cef93f99a54e6830e9717616ba6 (patch) | |
| tree | 066f25d70d5769148a84b0187619af7e6b765242 | |
| parent | dde5bff5415953f9cc7413f1b1ceebcdfd583c07 (diff) | |
| download | linux-1f9d3d98694b1cef93f99a54e6830e9717616ba6.tar.xz | |
ALSA: hda - set intel audio clock to a proper value
On some Intel platforms, the audio clock may not be set correctly
with initial setting. This will cause the audio playback/capture
rates wrong.
This patch checks the audio clock setting and will set it to a
proper value if it is set incorrectly.
Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=188411
Signed-off-by: Libin Yang <libin.yang@intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
| -rw-r--r-- | sound/pci/hda/hda_intel.c | 95 | 
1 files changed, 95 insertions, 0 deletions
| diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 64db6698214c..59ab34fa0bc8 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -540,6 +540,98 @@ static void bxt_reduce_dma_latency(struct azx *chip)  	azx_writel(chip, VS_EM4L, val);  } +/* + * ML_LCAP bits: + *  bit 0: 6 MHz Supported + *  bit 1: 12 MHz Supported + *  bit 2: 24 MHz Supported + *  bit 3: 48 MHz Supported + *  bit 4: 96 MHz Supported + *  bit 5: 192 MHz Supported + */ +static int intel_get_lctl_scf(struct azx *chip) +{ +	struct hdac_bus *bus = azx_bus(chip); +	static int preferred_bits[] = { 2, 3, 1, 4, 5 }; +	u32 val, t; +	int i; + +	val = readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCAP); + +	for (i = 0; i < ARRAY_SIZE(preferred_bits); i++) { +		t = preferred_bits[i]; +		if (val & (1 << t)) +			return t; +	} + +	dev_warn(chip->card->dev, "set audio clock frequency to 6MHz"); +	return 0; +} + +static int intel_ml_lctl_set_power(struct azx *chip, int state) +{ +	struct hdac_bus *bus = azx_bus(chip); +	u32 val; +	int timeout; + +	/* +	 * the codecs are sharing the first link setting by default +	 * If other links are enabled for stream, they need similar fix +	 */ +	val = readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL); +	val &= ~AZX_MLCTL_SPA; +	val |= state << AZX_MLCTL_SPA_SHIFT; +	writel(val, bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL); +	/* wait for CPA */ +	timeout = 50; +	while (timeout) { +		if (((readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL)) & +		    AZX_MLCTL_CPA) == (state << AZX_MLCTL_CPA_SHIFT)) +			return 0; +		timeout--; +		udelay(10); +	} + +	return -1; +} + +static void intel_init_lctl(struct azx *chip) +{ +	struct hdac_bus *bus = azx_bus(chip); +	u32 val; +	int ret; + +	/* 0. check lctl register value is correct or not */ +	val = readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL); +	/* if SCF is already set, let's use it */ +	if ((val & ML_LCTL_SCF_MASK) != 0) +		return; + +	/* +	 * Before operating on SPA, CPA must match SPA. +	 * Any deviation may result in undefined behavior. +	 */ +	if (((val & AZX_MLCTL_SPA) >> AZX_MLCTL_SPA_SHIFT) != +		((val & AZX_MLCTL_CPA) >> AZX_MLCTL_CPA_SHIFT)) +		return; + +	/* 1. turn link down: set SPA to 0 and wait CPA to 0 */ +	ret = intel_ml_lctl_set_power(chip, 0); +	udelay(100); +	if (ret) +		goto set_spa; + +	/* 2. update SCF to select a properly audio clock*/ +	val &= ~ML_LCTL_SCF_MASK; +	val |= intel_get_lctl_scf(chip); +	writel(val, bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL); + +set_spa: +	/* 4. turn link up: set SPA to 1 and wait CPA to 1 */ +	intel_ml_lctl_set_power(chip, 1); +	udelay(100); +} +  static void hda_intel_init_chip(struct azx *chip, bool full_reset)  {  	struct hdac_bus *bus = azx_bus(chip); @@ -565,6 +657,9 @@ static void hda_intel_init_chip(struct azx *chip, bool full_reset)  	/* reduce dma latency to avoid noise */  	if (IS_BXT(pci))  		bxt_reduce_dma_latency(chip); + +	if (bus->mlcap != NULL) +		intel_init_lctl(chip);  }  /* calculate runtime delay from LPIB */ | 
