summaryrefslogtreecommitdiffstats
path: root/sound/pci/hda/hda_intel.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2017-03-29 08:46:00 +0200
committerTakashi Iwai <tiwai@suse.de>2017-04-03 08:43:17 +0200
commitf87e7f25893d7db4da465c6b50882197e518d4af (patch)
tree1056926dc8c7862a3f071df4cb810da3131a7928 /sound/pci/hda/hda_intel.c
parent70eafad849f8af3a83e139eda36712d3c1da5b6a (diff)
downloadlinux-f87e7f25893d7db4da465c6b50882197e518d4af.tar.gz
linux-f87e7f25893d7db4da465c6b50882197e518d4af.tar.bz2
linux-f87e7f25893d7db4da465c6b50882197e518d4af.zip
ALSA: hda - Improved position reporting on SKL+
Apply the same methods to obtain the current stream position as ASoC Intel SKL driver uses. It reads the position from DPIB for a playback stream while it still reads from the position buffer for a capture stream. For a capture stream, some ugly workaround is needed to settle down the inconsistent position. Acked-by: Vinod Koul <vinod.koul@intel.com> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda/hda_intel.c')
-rw-r--r--sound/pci/hda/hda_intel.c36
1 files changed, 34 insertions, 2 deletions
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index a48330f4a1a9..64db6698214c 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -77,6 +77,7 @@ enum {
POS_FIX_POSBUF,
POS_FIX_VIACOMBO,
POS_FIX_COMBO,
+ POS_FIX_SKL,
};
/* Defines for ATI HD Audio support in SB450 south bridge */
@@ -148,7 +149,7 @@ module_param_array(model, charp, NULL, 0444);
MODULE_PARM_DESC(model, "Use the given board model.");
module_param_array(position_fix, int, NULL, 0444);
MODULE_PARM_DESC(position_fix, "DMA pointer read method."
- "(-1 = system default, 0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO, 4 = COMBO).");
+ "(-1 = system default, 0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO, 4 = COMBO, 5 = SKL+).");
module_param_array(bdl_pos_adj, int, NULL, 0644);
MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset.");
module_param_array(probe_mask, int, NULL, 0444);
@@ -815,6 +816,31 @@ static unsigned int azx_via_get_position(struct azx *chip,
return bound_pos + mod_dma_pos;
}
+static unsigned int azx_skl_get_dpib_pos(struct azx *chip,
+ struct azx_dev *azx_dev)
+{
+ return _snd_hdac_chip_readl(azx_bus(chip),
+ AZX_REG_VS_SDXDPIB_XBASE +
+ (AZX_REG_VS_SDXDPIB_XINTERVAL *
+ azx_dev->core.index));
+}
+
+/* get the current DMA position with correction on SKL+ chips */
+static unsigned int azx_get_pos_skl(struct azx *chip, struct azx_dev *azx_dev)
+{
+ /* DPIB register gives a more accurate position for playback */
+ if (azx_dev->core.substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ return azx_skl_get_dpib_pos(chip, azx_dev);
+
+ /* For capture, we need to read posbuf, but it requires a delay
+ * for the possible boundary overlap; the read of DPIB fetches the
+ * actual posbuf
+ */
+ udelay(20);
+ azx_skl_get_dpib_pos(chip, azx_dev);
+ return azx_get_pos_posbuf(chip, azx_dev);
+}
+
#ifdef CONFIG_PM
static DEFINE_MUTEX(card_list_lock);
static LIST_HEAD(card_list);
@@ -1351,6 +1377,7 @@ static int check_position_fix(struct azx *chip, int fix)
case POS_FIX_POSBUF:
case POS_FIX_VIACOMBO:
case POS_FIX_COMBO:
+ case POS_FIX_SKL:
return fix;
}
@@ -1371,6 +1398,10 @@ static int check_position_fix(struct azx *chip, int fix)
dev_dbg(chip->card->dev, "Using LPIB position fix\n");
return POS_FIX_LPIB;
}
+ if (IS_SKL_PLUS(chip->pci)) {
+ dev_dbg(chip->card->dev, "Using SKL position fix\n");
+ return POS_FIX_SKL;
+ }
return POS_FIX_AUTO;
}
@@ -1382,6 +1413,7 @@ static void assign_position_fix(struct azx *chip, int fix)
[POS_FIX_POSBUF] = azx_get_pos_posbuf,
[POS_FIX_VIACOMBO] = azx_via_get_position,
[POS_FIX_COMBO] = azx_get_pos_lpib,
+ [POS_FIX_SKL] = azx_get_pos_skl,
};
chip->get_position[0] = chip->get_position[1] = callbacks[fix];
@@ -1390,7 +1422,7 @@ static void assign_position_fix(struct azx *chip, int fix)
if (fix == POS_FIX_COMBO)
chip->get_position[1] = NULL;
- if (fix == POS_FIX_POSBUF &&
+ if ((fix == POS_FIX_POSBUF || fix == POS_FIX_SKL) &&
(chip->driver_caps & AZX_DCAPS_COUNT_LPIB_DELAY)) {
chip->get_delay[0] = chip->get_delay[1] =
azx_get_delay_from_lpib;