Skip to content

Commit

Permalink
ALSA: hda - set intel audio clock to a proper value
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: Takashi Iwai <[email protected]>
  • Loading branch information
libinyang authored and tiwai committed Apr 7, 2017
1 parent dde5bff commit 1f9d3d9
Showing 1 changed file with 95 additions and 0 deletions.
95 changes: 95 additions & 0 deletions sound/pci/hda/hda_intel.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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 */
Expand Down

0 comments on commit 1f9d3d9

Please sign in to comment.