162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// This file is provided under a dual BSD/GPLv2 license. When using or 462306a36Sopenharmony_ci// redistributing this file, you may do so under either license. 562306a36Sopenharmony_ci// 662306a36Sopenharmony_ci// Copyright(c) 2018 Intel Corporation. All rights reserved. 762306a36Sopenharmony_ci// 862306a36Sopenharmony_ci// Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com> 962306a36Sopenharmony_ci// Ranjani Sridharan <ranjani.sridharan@linux.intel.com> 1062306a36Sopenharmony_ci// Rander Wang <rander.wang@intel.com> 1162306a36Sopenharmony_ci// Keyon Jie <yang.jie@linux.intel.com> 1262306a36Sopenharmony_ci// 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/* 1562306a36Sopenharmony_ci * Hardware interface for generic Intel audio DSP HDA IP 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <sound/hdaudio_ext.h> 1962306a36Sopenharmony_ci#include <sound/hda_register.h> 2062306a36Sopenharmony_ci#include <sound/sof.h> 2162306a36Sopenharmony_ci#include <trace/events/sof_intel.h> 2262306a36Sopenharmony_ci#include "../ops.h" 2362306a36Sopenharmony_ci#include "../sof-audio.h" 2462306a36Sopenharmony_ci#include "hda.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define HDA_LTRP_GB_VALUE_US 95 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic inline const char *hda_hstream_direction_str(struct hdac_stream *hstream) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK) 3162306a36Sopenharmony_ci return "Playback"; 3262306a36Sopenharmony_ci else 3362306a36Sopenharmony_ci return "Capture"; 3462306a36Sopenharmony_ci} 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic char *hda_hstream_dbg_get_stream_info_str(struct hdac_stream *hstream) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if (hstream->substream) 4162306a36Sopenharmony_ci rtd = asoc_substream_to_rtd(hstream->substream); 4262306a36Sopenharmony_ci else if (hstream->cstream) 4362306a36Sopenharmony_ci rtd = hstream->cstream->private_data; 4462306a36Sopenharmony_ci else 4562306a36Sopenharmony_ci /* Non audio DMA user, like dma-trace */ 4662306a36Sopenharmony_ci return kasprintf(GFP_KERNEL, "-- (%s, stream_tag: %u)", 4762306a36Sopenharmony_ci hda_hstream_direction_str(hstream), 4862306a36Sopenharmony_ci hstream->stream_tag); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci return kasprintf(GFP_KERNEL, "dai_link \"%s\" (%s, stream_tag: %u)", 5162306a36Sopenharmony_ci rtd->dai_link->name, hda_hstream_direction_str(hstream), 5262306a36Sopenharmony_ci hstream->stream_tag); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* 5662306a36Sopenharmony_ci * set up one of BDL entries for a stream 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_cistatic int hda_setup_bdle(struct snd_sof_dev *sdev, 5962306a36Sopenharmony_ci struct snd_dma_buffer *dmab, 6062306a36Sopenharmony_ci struct hdac_stream *hstream, 6162306a36Sopenharmony_ci struct sof_intel_dsp_bdl **bdlp, 6262306a36Sopenharmony_ci int offset, int size, int ioc) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 6562306a36Sopenharmony_ci struct sof_intel_dsp_bdl *bdl = *bdlp; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci while (size > 0) { 6862306a36Sopenharmony_ci dma_addr_t addr; 6962306a36Sopenharmony_ci int chunk; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (hstream->frags >= HDA_DSP_MAX_BDL_ENTRIES) { 7262306a36Sopenharmony_ci dev_err(sdev->dev, "error: stream frags exceeded\n"); 7362306a36Sopenharmony_ci return -EINVAL; 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci addr = snd_sgbuf_get_addr(dmab, offset); 7762306a36Sopenharmony_ci /* program BDL addr */ 7862306a36Sopenharmony_ci bdl->addr_l = cpu_to_le32(lower_32_bits(addr)); 7962306a36Sopenharmony_ci bdl->addr_h = cpu_to_le32(upper_32_bits(addr)); 8062306a36Sopenharmony_ci /* program BDL size */ 8162306a36Sopenharmony_ci chunk = snd_sgbuf_get_chunk_size(dmab, offset, size); 8262306a36Sopenharmony_ci /* one BDLE should not cross 4K boundary */ 8362306a36Sopenharmony_ci if (bus->align_bdle_4k) { 8462306a36Sopenharmony_ci u32 remain = 0x1000 - (offset & 0xfff); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (chunk > remain) 8762306a36Sopenharmony_ci chunk = remain; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci bdl->size = cpu_to_le32(chunk); 9062306a36Sopenharmony_ci /* only program IOC when the whole segment is processed */ 9162306a36Sopenharmony_ci size -= chunk; 9262306a36Sopenharmony_ci bdl->ioc = (size || !ioc) ? 0 : cpu_to_le32(0x01); 9362306a36Sopenharmony_ci bdl++; 9462306a36Sopenharmony_ci hstream->frags++; 9562306a36Sopenharmony_ci offset += chunk; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci *bdlp = bdl; 9962306a36Sopenharmony_ci return offset; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/* 10362306a36Sopenharmony_ci * set up Buffer Descriptor List (BDL) for host memory transfer 10462306a36Sopenharmony_ci * BDL describes the location of the individual buffers and is little endian. 10562306a36Sopenharmony_ci */ 10662306a36Sopenharmony_ciint hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev, 10762306a36Sopenharmony_ci struct snd_dma_buffer *dmab, 10862306a36Sopenharmony_ci struct hdac_stream *hstream) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; 11162306a36Sopenharmony_ci struct sof_intel_dsp_bdl *bdl; 11262306a36Sopenharmony_ci int i, offset, period_bytes, periods; 11362306a36Sopenharmony_ci int remain, ioc; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci period_bytes = hstream->period_bytes; 11662306a36Sopenharmony_ci dev_dbg(sdev->dev, "period_bytes:0x%x\n", period_bytes); 11762306a36Sopenharmony_ci if (!period_bytes) 11862306a36Sopenharmony_ci period_bytes = hstream->bufsize; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci periods = hstream->bufsize / period_bytes; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci dev_dbg(sdev->dev, "periods:%d\n", periods); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci remain = hstream->bufsize % period_bytes; 12562306a36Sopenharmony_ci if (remain) 12662306a36Sopenharmony_ci periods++; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* program the initial BDL entries */ 12962306a36Sopenharmony_ci bdl = (struct sof_intel_dsp_bdl *)hstream->bdl.area; 13062306a36Sopenharmony_ci offset = 0; 13162306a36Sopenharmony_ci hstream->frags = 0; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* 13462306a36Sopenharmony_ci * set IOC if don't use position IPC 13562306a36Sopenharmony_ci * and period_wakeup needed. 13662306a36Sopenharmony_ci */ 13762306a36Sopenharmony_ci ioc = hda->no_ipc_position ? 13862306a36Sopenharmony_ci !hstream->no_period_wakeup : 0; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci for (i = 0; i < periods; i++) { 14162306a36Sopenharmony_ci if (i == (periods - 1) && remain) 14262306a36Sopenharmony_ci /* set the last small entry */ 14362306a36Sopenharmony_ci offset = hda_setup_bdle(sdev, dmab, 14462306a36Sopenharmony_ci hstream, &bdl, offset, 14562306a36Sopenharmony_ci remain, 0); 14662306a36Sopenharmony_ci else 14762306a36Sopenharmony_ci offset = hda_setup_bdle(sdev, dmab, 14862306a36Sopenharmony_ci hstream, &bdl, offset, 14962306a36Sopenharmony_ci period_bytes, ioc); 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return offset; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ciint hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, 15662306a36Sopenharmony_ci struct hdac_ext_stream *hext_stream, 15762306a36Sopenharmony_ci int enable, u32 size) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci struct hdac_stream *hstream = &hext_stream->hstream; 16062306a36Sopenharmony_ci u32 mask; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (!sdev->bar[HDA_DSP_SPIB_BAR]) { 16362306a36Sopenharmony_ci dev_err(sdev->dev, "error: address of spib capability is NULL\n"); 16462306a36Sopenharmony_ci return -EINVAL; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci mask = (1 << hstream->index); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* enable/disable SPIB for the stream */ 17062306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_SPIB_BAR, 17162306a36Sopenharmony_ci SOF_HDA_ADSP_REG_CL_SPBFIFO_SPBFCCTL, mask, 17262306a36Sopenharmony_ci enable << hstream->index); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* set the SPIB value */ 17562306a36Sopenharmony_ci sof_io_write(sdev, hstream->spib_addr, size); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci return 0; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci/* get next unused stream */ 18162306a36Sopenharmony_cistruct hdac_ext_stream * 18262306a36Sopenharmony_cihda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci const struct sof_intel_dsp_desc *chip_info = get_chip_info(sdev->pdata); 18562306a36Sopenharmony_ci struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; 18662306a36Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 18762306a36Sopenharmony_ci struct sof_intel_hda_stream *hda_stream; 18862306a36Sopenharmony_ci struct hdac_ext_stream *hext_stream = NULL; 18962306a36Sopenharmony_ci struct hdac_stream *s; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci spin_lock_irq(&bus->reg_lock); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* get an unused stream */ 19462306a36Sopenharmony_ci list_for_each_entry(s, &bus->stream_list, list) { 19562306a36Sopenharmony_ci if (s->direction == direction && !s->opened) { 19662306a36Sopenharmony_ci hext_stream = stream_to_hdac_ext_stream(s); 19762306a36Sopenharmony_ci hda_stream = container_of(hext_stream, 19862306a36Sopenharmony_ci struct sof_intel_hda_stream, 19962306a36Sopenharmony_ci hext_stream); 20062306a36Sopenharmony_ci /* check if the host DMA channel is reserved */ 20162306a36Sopenharmony_ci if (hda_stream->host_reserved) 20262306a36Sopenharmony_ci continue; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci s->opened = true; 20562306a36Sopenharmony_ci break; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci spin_unlock_irq(&bus->reg_lock); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* stream found ? */ 21262306a36Sopenharmony_ci if (!hext_stream) { 21362306a36Sopenharmony_ci dev_err(sdev->dev, "error: no free %s streams\n", 21462306a36Sopenharmony_ci direction == SNDRV_PCM_STREAM_PLAYBACK ? 21562306a36Sopenharmony_ci "playback" : "capture"); 21662306a36Sopenharmony_ci return hext_stream; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci hda_stream->flags = flags; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* 22262306a36Sopenharmony_ci * Prevent DMI Link L1 entry for streams that don't support it. 22362306a36Sopenharmony_ci * Workaround to address a known issue with host DMA that results 22462306a36Sopenharmony_ci * in xruns during pause/release in capture scenarios. This is not needed for the ACE IP. 22562306a36Sopenharmony_ci */ 22662306a36Sopenharmony_ci if (chip_info->hw_ip_version < SOF_INTEL_ACE_1_0 && 22762306a36Sopenharmony_ci !(flags & SOF_HDA_STREAM_DMI_L1_COMPATIBLE)) { 22862306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, 22962306a36Sopenharmony_ci HDA_VS_INTEL_EM2, 23062306a36Sopenharmony_ci HDA_VS_INTEL_EM2_L1SEN, 0); 23162306a36Sopenharmony_ci hda->l1_disabled = true; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci return hext_stream; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci/* free a stream */ 23862306a36Sopenharmony_ciint hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci const struct sof_intel_dsp_desc *chip_info = get_chip_info(sdev->pdata); 24162306a36Sopenharmony_ci struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; 24262306a36Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 24362306a36Sopenharmony_ci struct sof_intel_hda_stream *hda_stream; 24462306a36Sopenharmony_ci struct hdac_ext_stream *hext_stream; 24562306a36Sopenharmony_ci struct hdac_stream *s; 24662306a36Sopenharmony_ci bool dmi_l1_enable = true; 24762306a36Sopenharmony_ci bool found = false; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci spin_lock_irq(&bus->reg_lock); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* 25262306a36Sopenharmony_ci * close stream matching the stream tag and check if there are any open streams 25362306a36Sopenharmony_ci * that are DMI L1 incompatible. 25462306a36Sopenharmony_ci */ 25562306a36Sopenharmony_ci list_for_each_entry(s, &bus->stream_list, list) { 25662306a36Sopenharmony_ci hext_stream = stream_to_hdac_ext_stream(s); 25762306a36Sopenharmony_ci hda_stream = container_of(hext_stream, struct sof_intel_hda_stream, hext_stream); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (!s->opened) 26062306a36Sopenharmony_ci continue; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (s->direction == direction && s->stream_tag == stream_tag) { 26362306a36Sopenharmony_ci s->opened = false; 26462306a36Sopenharmony_ci found = true; 26562306a36Sopenharmony_ci } else if (!(hda_stream->flags & SOF_HDA_STREAM_DMI_L1_COMPATIBLE)) { 26662306a36Sopenharmony_ci dmi_l1_enable = false; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci spin_unlock_irq(&bus->reg_lock); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci /* Enable DMI L1 if permitted */ 27362306a36Sopenharmony_ci if (chip_info->hw_ip_version < SOF_INTEL_ACE_1_0 && dmi_l1_enable) { 27462306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_EM2, 27562306a36Sopenharmony_ci HDA_VS_INTEL_EM2_L1SEN, HDA_VS_INTEL_EM2_L1SEN); 27662306a36Sopenharmony_ci hda->l1_disabled = false; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (!found) { 28062306a36Sopenharmony_ci dev_err(sdev->dev, "%s: stream_tag %d not opened!\n", 28162306a36Sopenharmony_ci __func__, stream_tag); 28262306a36Sopenharmony_ci return -ENODEV; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci return 0; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic int hda_dsp_stream_reset(struct snd_sof_dev *sdev, struct hdac_stream *hstream) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci int sd_offset = SOF_STREAM_SD_OFFSET(hstream); 29162306a36Sopenharmony_ci int timeout = HDA_DSP_STREAM_RESET_TIMEOUT; 29262306a36Sopenharmony_ci u32 val; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* enter stream reset */ 29562306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, SOF_STREAM_SD_OFFSET_CRST, 29662306a36Sopenharmony_ci SOF_STREAM_SD_OFFSET_CRST); 29762306a36Sopenharmony_ci do { 29862306a36Sopenharmony_ci val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, sd_offset); 29962306a36Sopenharmony_ci if (val & SOF_STREAM_SD_OFFSET_CRST) 30062306a36Sopenharmony_ci break; 30162306a36Sopenharmony_ci } while (--timeout); 30262306a36Sopenharmony_ci if (timeout == 0) { 30362306a36Sopenharmony_ci dev_err(sdev->dev, "timeout waiting for stream reset\n"); 30462306a36Sopenharmony_ci return -ETIMEDOUT; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci timeout = HDA_DSP_STREAM_RESET_TIMEOUT; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* exit stream reset and wait to read a zero before reading any other register */ 31062306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, SOF_STREAM_SD_OFFSET_CRST, 0x0); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci /* wait for hardware to report that stream is out of reset */ 31362306a36Sopenharmony_ci udelay(3); 31462306a36Sopenharmony_ci do { 31562306a36Sopenharmony_ci val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, sd_offset); 31662306a36Sopenharmony_ci if ((val & SOF_STREAM_SD_OFFSET_CRST) == 0) 31762306a36Sopenharmony_ci break; 31862306a36Sopenharmony_ci } while (--timeout); 31962306a36Sopenharmony_ci if (timeout == 0) { 32062306a36Sopenharmony_ci dev_err(sdev->dev, "timeout waiting for stream to exit reset\n"); 32162306a36Sopenharmony_ci return -ETIMEDOUT; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci return 0; 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ciint hda_dsp_stream_trigger(struct snd_sof_dev *sdev, 32862306a36Sopenharmony_ci struct hdac_ext_stream *hext_stream, int cmd) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci struct hdac_stream *hstream = &hext_stream->hstream; 33162306a36Sopenharmony_ci int sd_offset = SOF_STREAM_SD_OFFSET(hstream); 33262306a36Sopenharmony_ci u32 dma_start = SOF_HDA_SD_CTL_DMA_START; 33362306a36Sopenharmony_ci int ret = 0; 33462306a36Sopenharmony_ci u32 run; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci /* cmd must be for audio stream */ 33762306a36Sopenharmony_ci switch (cmd) { 33862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 33962306a36Sopenharmony_ci if (!sdev->dspless_mode_selected) 34062306a36Sopenharmony_ci break; 34162306a36Sopenharmony_ci fallthrough; 34262306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 34362306a36Sopenharmony_ci if (hstream->running) 34462306a36Sopenharmony_ci break; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, 34762306a36Sopenharmony_ci 1 << hstream->index, 34862306a36Sopenharmony_ci 1 << hstream->index); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, 35162306a36Sopenharmony_ci sd_offset, 35262306a36Sopenharmony_ci SOF_HDA_SD_CTL_DMA_START | 35362306a36Sopenharmony_ci SOF_HDA_CL_DMA_SD_INT_MASK, 35462306a36Sopenharmony_ci SOF_HDA_SD_CTL_DMA_START | 35562306a36Sopenharmony_ci SOF_HDA_CL_DMA_SD_INT_MASK); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci ret = snd_sof_dsp_read_poll_timeout(sdev, 35862306a36Sopenharmony_ci HDA_DSP_HDA_BAR, 35962306a36Sopenharmony_ci sd_offset, run, 36062306a36Sopenharmony_ci ((run & dma_start) == dma_start), 36162306a36Sopenharmony_ci HDA_DSP_REG_POLL_INTERVAL_US, 36262306a36Sopenharmony_ci HDA_DSP_STREAM_RUN_TIMEOUT); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (ret >= 0) 36562306a36Sopenharmony_ci hstream->running = true; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci break; 36862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 36962306a36Sopenharmony_ci if (!sdev->dspless_mode_selected) 37062306a36Sopenharmony_ci break; 37162306a36Sopenharmony_ci fallthrough; 37262306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 37362306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 37462306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, 37562306a36Sopenharmony_ci sd_offset, 37662306a36Sopenharmony_ci SOF_HDA_SD_CTL_DMA_START | 37762306a36Sopenharmony_ci SOF_HDA_CL_DMA_SD_INT_MASK, 0x0); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_HDA_BAR, 38062306a36Sopenharmony_ci sd_offset, run, 38162306a36Sopenharmony_ci !(run & dma_start), 38262306a36Sopenharmony_ci HDA_DSP_REG_POLL_INTERVAL_US, 38362306a36Sopenharmony_ci HDA_DSP_STREAM_RUN_TIMEOUT); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (ret >= 0) { 38662306a36Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, 38762306a36Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_SD_STS, 38862306a36Sopenharmony_ci SOF_HDA_CL_DMA_SD_INT_MASK); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci hstream->running = false; 39162306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, 39262306a36Sopenharmony_ci SOF_HDA_INTCTL, 39362306a36Sopenharmony_ci 1 << hstream->index, 0x0); 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci break; 39662306a36Sopenharmony_ci default: 39762306a36Sopenharmony_ci dev_err(sdev->dev, "error: unknown command: %d\n", cmd); 39862306a36Sopenharmony_ci return -EINVAL; 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci if (ret < 0) { 40262306a36Sopenharmony_ci char *stream_name = hda_hstream_dbg_get_stream_info_str(hstream); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci dev_err(sdev->dev, 40562306a36Sopenharmony_ci "%s: cmd %d on %s: timeout on STREAM_SD_OFFSET read\n", 40662306a36Sopenharmony_ci __func__, cmd, stream_name ? stream_name : "unknown stream"); 40762306a36Sopenharmony_ci kfree(stream_name); 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci return ret; 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci/* minimal recommended programming for ICCMAX stream */ 41462306a36Sopenharmony_ciint hda_dsp_iccmax_stream_hw_params(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream, 41562306a36Sopenharmony_ci struct snd_dma_buffer *dmab, 41662306a36Sopenharmony_ci struct snd_pcm_hw_params *params) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci struct hdac_stream *hstream = &hext_stream->hstream; 41962306a36Sopenharmony_ci int sd_offset = SOF_STREAM_SD_OFFSET(hstream); 42062306a36Sopenharmony_ci int ret; 42162306a36Sopenharmony_ci u32 mask = 0x1 << hstream->index; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (!hext_stream) { 42462306a36Sopenharmony_ci dev_err(sdev->dev, "error: no stream available\n"); 42562306a36Sopenharmony_ci return -ENODEV; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (!dmab) { 42962306a36Sopenharmony_ci dev_err(sdev->dev, "error: no dma buffer allocated!\n"); 43062306a36Sopenharmony_ci return -ENODEV; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if (hstream->posbuf) 43462306a36Sopenharmony_ci *hstream->posbuf = 0; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci /* reset BDL address */ 43762306a36Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, 43862306a36Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_SD_BDLPL, 43962306a36Sopenharmony_ci 0x0); 44062306a36Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, 44162306a36Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU, 44262306a36Sopenharmony_ci 0x0); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci hstream->frags = 0; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci ret = hda_dsp_stream_setup_bdl(sdev, dmab, hstream); 44762306a36Sopenharmony_ci if (ret < 0) { 44862306a36Sopenharmony_ci dev_err(sdev->dev, "error: set up of BDL failed\n"); 44962306a36Sopenharmony_ci return ret; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci /* program BDL address */ 45362306a36Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, 45462306a36Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_SD_BDLPL, 45562306a36Sopenharmony_ci (u32)hstream->bdl.addr); 45662306a36Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, 45762306a36Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU, 45862306a36Sopenharmony_ci upper_32_bits(hstream->bdl.addr)); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* program cyclic buffer length */ 46162306a36Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, 46262306a36Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_SD_CBL, 46362306a36Sopenharmony_ci hstream->bufsize); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci /* program last valid index */ 46662306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, 46762306a36Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_SD_LVI, 46862306a36Sopenharmony_ci 0xffff, (hstream->frags - 1)); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci /* decouple host and link DMA, enable DSP features */ 47162306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, 47262306a36Sopenharmony_ci mask, mask); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci /* Follow HW recommendation to set the guardband value to 95us during FW boot */ 47562306a36Sopenharmony_ci snd_sof_dsp_update8(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_LTRP, 47662306a36Sopenharmony_ci HDA_VS_INTEL_LTRP_GB_MASK, HDA_LTRP_GB_VALUE_US); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci /* start DMA */ 47962306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 48062306a36Sopenharmony_ci SOF_HDA_SD_CTL_DMA_START, SOF_HDA_SD_CTL_DMA_START); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci return 0; 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci/* 48662306a36Sopenharmony_ci * prepare for common hdac registers settings, for both code loader 48762306a36Sopenharmony_ci * and normal stream. 48862306a36Sopenharmony_ci */ 48962306a36Sopenharmony_ciint hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, 49062306a36Sopenharmony_ci struct hdac_ext_stream *hext_stream, 49162306a36Sopenharmony_ci struct snd_dma_buffer *dmab, 49262306a36Sopenharmony_ci struct snd_pcm_hw_params *params) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci const struct sof_intel_dsp_desc *chip = get_chip_info(sdev->pdata); 49562306a36Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 49662306a36Sopenharmony_ci struct hdac_stream *hstream; 49762306a36Sopenharmony_ci int sd_offset, ret; 49862306a36Sopenharmony_ci u32 dma_start = SOF_HDA_SD_CTL_DMA_START; 49962306a36Sopenharmony_ci u32 mask; 50062306a36Sopenharmony_ci u32 run; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (!hext_stream) { 50362306a36Sopenharmony_ci dev_err(sdev->dev, "error: no stream available\n"); 50462306a36Sopenharmony_ci return -ENODEV; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci if (!dmab) { 50862306a36Sopenharmony_ci dev_err(sdev->dev, "error: no dma buffer allocated!\n"); 50962306a36Sopenharmony_ci return -ENODEV; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci hstream = &hext_stream->hstream; 51362306a36Sopenharmony_ci sd_offset = SOF_STREAM_SD_OFFSET(hstream); 51462306a36Sopenharmony_ci mask = BIT(hstream->index); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci /* decouple host and link DMA if the DSP is used */ 51762306a36Sopenharmony_ci if (!sdev->dspless_mode_selected) 51862306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, 51962306a36Sopenharmony_ci mask, mask); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci /* clear stream status */ 52262306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 52362306a36Sopenharmony_ci SOF_HDA_CL_DMA_SD_INT_MASK | 52462306a36Sopenharmony_ci SOF_HDA_SD_CTL_DMA_START, 0); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_HDA_BAR, 52762306a36Sopenharmony_ci sd_offset, run, 52862306a36Sopenharmony_ci !(run & dma_start), 52962306a36Sopenharmony_ci HDA_DSP_REG_POLL_INTERVAL_US, 53062306a36Sopenharmony_ci HDA_DSP_STREAM_RUN_TIMEOUT); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci if (ret < 0) { 53362306a36Sopenharmony_ci char *stream_name = hda_hstream_dbg_get_stream_info_str(hstream); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci dev_err(sdev->dev, 53662306a36Sopenharmony_ci "%s: on %s: timeout on STREAM_SD_OFFSET read1\n", 53762306a36Sopenharmony_ci __func__, stream_name ? stream_name : "unknown stream"); 53862306a36Sopenharmony_ci kfree(stream_name); 53962306a36Sopenharmony_ci return ret; 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, 54362306a36Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_SD_STS, 54462306a36Sopenharmony_ci SOF_HDA_CL_DMA_SD_INT_MASK, 54562306a36Sopenharmony_ci SOF_HDA_CL_DMA_SD_INT_MASK); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci /* stream reset */ 54862306a36Sopenharmony_ci ret = hda_dsp_stream_reset(sdev, hstream); 54962306a36Sopenharmony_ci if (ret < 0) 55062306a36Sopenharmony_ci return ret; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci if (hstream->posbuf) 55362306a36Sopenharmony_ci *hstream->posbuf = 0; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci /* reset BDL address */ 55662306a36Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, 55762306a36Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_SD_BDLPL, 55862306a36Sopenharmony_ci 0x0); 55962306a36Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, 56062306a36Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU, 56162306a36Sopenharmony_ci 0x0); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci /* clear stream status */ 56462306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 56562306a36Sopenharmony_ci SOF_HDA_CL_DMA_SD_INT_MASK | 56662306a36Sopenharmony_ci SOF_HDA_SD_CTL_DMA_START, 0); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_HDA_BAR, 56962306a36Sopenharmony_ci sd_offset, run, 57062306a36Sopenharmony_ci !(run & dma_start), 57162306a36Sopenharmony_ci HDA_DSP_REG_POLL_INTERVAL_US, 57262306a36Sopenharmony_ci HDA_DSP_STREAM_RUN_TIMEOUT); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci if (ret < 0) { 57562306a36Sopenharmony_ci char *stream_name = hda_hstream_dbg_get_stream_info_str(hstream); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci dev_err(sdev->dev, 57862306a36Sopenharmony_ci "%s: on %s: timeout on STREAM_SD_OFFSET read1\n", 57962306a36Sopenharmony_ci __func__, stream_name ? stream_name : "unknown stream"); 58062306a36Sopenharmony_ci kfree(stream_name); 58162306a36Sopenharmony_ci return ret; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, 58562306a36Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_SD_STS, 58662306a36Sopenharmony_ci SOF_HDA_CL_DMA_SD_INT_MASK, 58762306a36Sopenharmony_ci SOF_HDA_CL_DMA_SD_INT_MASK); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci hstream->frags = 0; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci ret = hda_dsp_stream_setup_bdl(sdev, dmab, hstream); 59262306a36Sopenharmony_ci if (ret < 0) { 59362306a36Sopenharmony_ci dev_err(sdev->dev, "error: set up of BDL failed\n"); 59462306a36Sopenharmony_ci return ret; 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci /* program stream tag to set up stream descriptor for DMA */ 59862306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 59962306a36Sopenharmony_ci SOF_HDA_CL_SD_CTL_STREAM_TAG_MASK, 60062306a36Sopenharmony_ci hstream->stream_tag << 60162306a36Sopenharmony_ci SOF_HDA_CL_SD_CTL_STREAM_TAG_SHIFT); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci /* program cyclic buffer length */ 60462306a36Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, 60562306a36Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_SD_CBL, 60662306a36Sopenharmony_ci hstream->bufsize); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci /* 60962306a36Sopenharmony_ci * Recommended hardware programming sequence for HDAudio DMA format 61062306a36Sopenharmony_ci * on earlier platforms - this is not needed on newer platforms 61162306a36Sopenharmony_ci * 61262306a36Sopenharmony_ci * 1. Put DMA into coupled mode by clearing PPCTL.PROCEN bit 61362306a36Sopenharmony_ci * for corresponding stream index before the time of writing 61462306a36Sopenharmony_ci * format to SDxFMT register. 61562306a36Sopenharmony_ci * 2. Write SDxFMT 61662306a36Sopenharmony_ci * 3. Set PPCTL.PROCEN bit for corresponding stream index to 61762306a36Sopenharmony_ci * enable decoupled mode 61862306a36Sopenharmony_ci */ 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci if (!sdev->dspless_mode_selected && (chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK)) 62162306a36Sopenharmony_ci /* couple host and link DMA, disable DSP features */ 62262306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, 62362306a36Sopenharmony_ci mask, 0); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci /* program stream format */ 62662306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, 62762306a36Sopenharmony_ci sd_offset + 62862306a36Sopenharmony_ci SOF_HDA_ADSP_REG_SD_FORMAT, 62962306a36Sopenharmony_ci 0xffff, hstream->format_val); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci if (!sdev->dspless_mode_selected && (chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK)) 63262306a36Sopenharmony_ci /* decouple host and link DMA, enable DSP features */ 63362306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, 63462306a36Sopenharmony_ci mask, mask); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci /* program last valid index */ 63762306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, 63862306a36Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_SD_LVI, 63962306a36Sopenharmony_ci 0xffff, (hstream->frags - 1)); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci /* program BDL address */ 64262306a36Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, 64362306a36Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_SD_BDLPL, 64462306a36Sopenharmony_ci (u32)hstream->bdl.addr); 64562306a36Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, 64662306a36Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU, 64762306a36Sopenharmony_ci upper_32_bits(hstream->bdl.addr)); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci /* enable position buffer, if needed */ 65062306a36Sopenharmony_ci if (bus->use_posbuf && bus->posbuf.addr && 65162306a36Sopenharmony_ci !(snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE) 65262306a36Sopenharmony_ci & SOF_HDA_ADSP_DPLBASE_ENABLE)) { 65362306a36Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPUBASE, 65462306a36Sopenharmony_ci upper_32_bits(bus->posbuf.addr)); 65562306a36Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE, 65662306a36Sopenharmony_ci (u32)bus->posbuf.addr | 65762306a36Sopenharmony_ci SOF_HDA_ADSP_DPLBASE_ENABLE); 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci /* set interrupt enable bits */ 66162306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 66262306a36Sopenharmony_ci SOF_HDA_CL_DMA_SD_INT_MASK, 66362306a36Sopenharmony_ci SOF_HDA_CL_DMA_SD_INT_MASK); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci /* read FIFO size */ 66662306a36Sopenharmony_ci if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK) { 66762306a36Sopenharmony_ci hstream->fifo_size = 66862306a36Sopenharmony_ci snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, 66962306a36Sopenharmony_ci sd_offset + 67062306a36Sopenharmony_ci SOF_HDA_ADSP_REG_SD_FIFOSIZE); 67162306a36Sopenharmony_ci hstream->fifo_size &= 0xffff; 67262306a36Sopenharmony_ci hstream->fifo_size += 1; 67362306a36Sopenharmony_ci } else { 67462306a36Sopenharmony_ci hstream->fifo_size = 0; 67562306a36Sopenharmony_ci } 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci return ret; 67862306a36Sopenharmony_ci} 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ciint hda_dsp_stream_hw_free(struct snd_sof_dev *sdev, 68162306a36Sopenharmony_ci struct snd_pcm_substream *substream) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci struct hdac_stream *hstream = substream->runtime->private_data; 68462306a36Sopenharmony_ci struct hdac_ext_stream *hext_stream = container_of(hstream, 68562306a36Sopenharmony_ci struct hdac_ext_stream, 68662306a36Sopenharmony_ci hstream); 68762306a36Sopenharmony_ci int ret; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci ret = hda_dsp_stream_reset(sdev, hstream); 69062306a36Sopenharmony_ci if (ret < 0) 69162306a36Sopenharmony_ci return ret; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci if (!sdev->dspless_mode_selected) { 69462306a36Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 69562306a36Sopenharmony_ci u32 mask = BIT(hstream->index); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci spin_lock_irq(&bus->reg_lock); 69862306a36Sopenharmony_ci /* couple host and link DMA if link DMA channel is idle */ 69962306a36Sopenharmony_ci if (!hext_stream->link_locked) 70062306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, 70162306a36Sopenharmony_ci SOF_HDA_REG_PP_PPCTL, mask, 0); 70262306a36Sopenharmony_ci spin_unlock_irq(&bus->reg_lock); 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci hstream->substream = NULL; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci return 0; 71062306a36Sopenharmony_ci} 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cibool hda_dsp_check_stream_irq(struct snd_sof_dev *sdev) 71362306a36Sopenharmony_ci{ 71462306a36Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 71562306a36Sopenharmony_ci bool ret = false; 71662306a36Sopenharmony_ci u32 status; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci /* The function can be called at irq thread, so use spin_lock_irq */ 71962306a36Sopenharmony_ci spin_lock_irq(&bus->reg_lock); 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci status = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci trace_sof_intel_hda_dsp_check_stream_irq(sdev, status); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci /* if Register inaccessible, ignore it.*/ 72662306a36Sopenharmony_ci if (status != 0xffffffff) 72762306a36Sopenharmony_ci ret = true; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci spin_unlock_irq(&bus->reg_lock); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci return ret; 73262306a36Sopenharmony_ci} 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_cistatic void 73562306a36Sopenharmony_cihda_dsp_compr_bytes_transferred(struct hdac_stream *hstream, int direction) 73662306a36Sopenharmony_ci{ 73762306a36Sopenharmony_ci u64 buffer_size = hstream->bufsize; 73862306a36Sopenharmony_ci u64 prev_pos, pos, num_bytes; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci div64_u64_rem(hstream->curr_pos, buffer_size, &prev_pos); 74162306a36Sopenharmony_ci pos = hda_dsp_stream_get_position(hstream, direction, false); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (pos < prev_pos) 74462306a36Sopenharmony_ci num_bytes = (buffer_size - prev_pos) + pos; 74562306a36Sopenharmony_ci else 74662306a36Sopenharmony_ci num_bytes = pos - prev_pos; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci hstream->curr_pos += num_bytes; 74962306a36Sopenharmony_ci} 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_cistatic bool hda_dsp_stream_check(struct hdac_bus *bus, u32 status) 75262306a36Sopenharmony_ci{ 75362306a36Sopenharmony_ci struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus); 75462306a36Sopenharmony_ci struct hdac_stream *s; 75562306a36Sopenharmony_ci bool active = false; 75662306a36Sopenharmony_ci u32 sd_status; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci list_for_each_entry(s, &bus->stream_list, list) { 75962306a36Sopenharmony_ci if (status & BIT(s->index) && s->opened) { 76062306a36Sopenharmony_ci sd_status = readb(s->sd_addr + SOF_HDA_ADSP_REG_SD_STS); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci trace_sof_intel_hda_dsp_stream_status(bus->dev, s, sd_status); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci writeb(sd_status, s->sd_addr + SOF_HDA_ADSP_REG_SD_STS); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci active = true; 76762306a36Sopenharmony_ci if ((!s->substream && !s->cstream) || 76862306a36Sopenharmony_ci !s->running || 76962306a36Sopenharmony_ci (sd_status & SOF_HDA_CL_DMA_SD_INT_COMPLETE) == 0) 77062306a36Sopenharmony_ci continue; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci /* Inform ALSA only in case not do that with IPC */ 77362306a36Sopenharmony_ci if (s->substream && sof_hda->no_ipc_position) { 77462306a36Sopenharmony_ci snd_sof_pcm_period_elapsed(s->substream); 77562306a36Sopenharmony_ci } else if (s->cstream) { 77662306a36Sopenharmony_ci hda_dsp_compr_bytes_transferred(s, s->cstream->direction); 77762306a36Sopenharmony_ci snd_compr_fragment_elapsed(s->cstream); 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci } 78062306a36Sopenharmony_ci } 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci return active; 78362306a36Sopenharmony_ci} 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ciirqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context) 78662306a36Sopenharmony_ci{ 78762306a36Sopenharmony_ci struct snd_sof_dev *sdev = context; 78862306a36Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 78962306a36Sopenharmony_ci bool active; 79062306a36Sopenharmony_ci u32 status; 79162306a36Sopenharmony_ci int i; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci /* 79462306a36Sopenharmony_ci * Loop 10 times to handle missed interrupts caused by 79562306a36Sopenharmony_ci * unsolicited responses from the codec 79662306a36Sopenharmony_ci */ 79762306a36Sopenharmony_ci for (i = 0, active = true; i < 10 && active; i++) { 79862306a36Sopenharmony_ci spin_lock_irq(&bus->reg_lock); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci status = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci /* check streams */ 80362306a36Sopenharmony_ci active = hda_dsp_stream_check(bus, status); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci /* check and clear RIRB interrupt */ 80662306a36Sopenharmony_ci if (status & AZX_INT_CTRL_EN) { 80762306a36Sopenharmony_ci active |= hda_codec_check_rirb_status(sdev); 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci spin_unlock_irq(&bus->reg_lock); 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci return IRQ_HANDLED; 81362306a36Sopenharmony_ci} 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ciint hda_dsp_stream_init(struct snd_sof_dev *sdev) 81662306a36Sopenharmony_ci{ 81762306a36Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 81862306a36Sopenharmony_ci struct hdac_ext_stream *hext_stream; 81962306a36Sopenharmony_ci struct hdac_stream *hstream; 82062306a36Sopenharmony_ci struct pci_dev *pci = to_pci_dev(sdev->dev); 82162306a36Sopenharmony_ci struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus); 82262306a36Sopenharmony_ci int sd_offset; 82362306a36Sopenharmony_ci int i, num_playback, num_capture, num_total, ret; 82462306a36Sopenharmony_ci u32 gcap; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci gcap = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_GCAP); 82762306a36Sopenharmony_ci dev_dbg(sdev->dev, "hda global caps = 0x%x\n", gcap); 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci /* get stream count from GCAP */ 83062306a36Sopenharmony_ci num_capture = (gcap >> 8) & 0x0f; 83162306a36Sopenharmony_ci num_playback = (gcap >> 12) & 0x0f; 83262306a36Sopenharmony_ci num_total = num_playback + num_capture; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci dev_dbg(sdev->dev, "detected %d playback and %d capture streams\n", 83562306a36Sopenharmony_ci num_playback, num_capture); 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci if (num_playback >= SOF_HDA_PLAYBACK_STREAMS) { 83862306a36Sopenharmony_ci dev_err(sdev->dev, "error: too many playback streams %d\n", 83962306a36Sopenharmony_ci num_playback); 84062306a36Sopenharmony_ci return -EINVAL; 84162306a36Sopenharmony_ci } 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci if (num_capture >= SOF_HDA_CAPTURE_STREAMS) { 84462306a36Sopenharmony_ci dev_err(sdev->dev, "error: too many capture streams %d\n", 84562306a36Sopenharmony_ci num_playback); 84662306a36Sopenharmony_ci return -EINVAL; 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci /* 85062306a36Sopenharmony_ci * mem alloc for the position buffer 85162306a36Sopenharmony_ci * TODO: check position buffer update 85262306a36Sopenharmony_ci */ 85362306a36Sopenharmony_ci ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, 85462306a36Sopenharmony_ci SOF_HDA_DPIB_ENTRY_SIZE * num_total, 85562306a36Sopenharmony_ci &bus->posbuf); 85662306a36Sopenharmony_ci if (ret < 0) { 85762306a36Sopenharmony_ci dev_err(sdev->dev, "error: posbuffer dma alloc failed\n"); 85862306a36Sopenharmony_ci return -ENOMEM; 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci /* 86262306a36Sopenharmony_ci * mem alloc for the CORB/RIRB ringbuffers - this will be used only for 86362306a36Sopenharmony_ci * HDAudio codecs 86462306a36Sopenharmony_ci */ 86562306a36Sopenharmony_ci ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, 86662306a36Sopenharmony_ci PAGE_SIZE, &bus->rb); 86762306a36Sopenharmony_ci if (ret < 0) { 86862306a36Sopenharmony_ci dev_err(sdev->dev, "error: RB alloc failed\n"); 86962306a36Sopenharmony_ci return -ENOMEM; 87062306a36Sopenharmony_ci } 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci /* create capture and playback streams */ 87362306a36Sopenharmony_ci for (i = 0; i < num_total; i++) { 87462306a36Sopenharmony_ci struct sof_intel_hda_stream *hda_stream; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci hda_stream = devm_kzalloc(sdev->dev, sizeof(*hda_stream), 87762306a36Sopenharmony_ci GFP_KERNEL); 87862306a36Sopenharmony_ci if (!hda_stream) 87962306a36Sopenharmony_ci return -ENOMEM; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci hda_stream->sdev = sdev; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci hext_stream = &hda_stream->hext_stream; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci if (sdev->bar[HDA_DSP_PP_BAR]) { 88662306a36Sopenharmony_ci hext_stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] + 88762306a36Sopenharmony_ci SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci hext_stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] + 89062306a36Sopenharmony_ci SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total + 89162306a36Sopenharmony_ci SOF_HDA_PPLC_INTERVAL * i; 89262306a36Sopenharmony_ci } 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci hstream = &hext_stream->hstream; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci /* do we support SPIB */ 89762306a36Sopenharmony_ci if (sdev->bar[HDA_DSP_SPIB_BAR]) { 89862306a36Sopenharmony_ci hstream->spib_addr = sdev->bar[HDA_DSP_SPIB_BAR] + 89962306a36Sopenharmony_ci SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i + 90062306a36Sopenharmony_ci SOF_HDA_SPIB_SPIB; 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci hstream->fifo_addr = sdev->bar[HDA_DSP_SPIB_BAR] + 90362306a36Sopenharmony_ci SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i + 90462306a36Sopenharmony_ci SOF_HDA_SPIB_MAXFIFO; 90562306a36Sopenharmony_ci } 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci hstream->bus = bus; 90862306a36Sopenharmony_ci hstream->sd_int_sta_mask = 1 << i; 90962306a36Sopenharmony_ci hstream->index = i; 91062306a36Sopenharmony_ci sd_offset = SOF_STREAM_SD_OFFSET(hstream); 91162306a36Sopenharmony_ci hstream->sd_addr = sdev->bar[HDA_DSP_HDA_BAR] + sd_offset; 91262306a36Sopenharmony_ci hstream->opened = false; 91362306a36Sopenharmony_ci hstream->running = false; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci if (i < num_capture) { 91662306a36Sopenharmony_ci hstream->stream_tag = i + 1; 91762306a36Sopenharmony_ci hstream->direction = SNDRV_PCM_STREAM_CAPTURE; 91862306a36Sopenharmony_ci } else { 91962306a36Sopenharmony_ci hstream->stream_tag = i - num_capture + 1; 92062306a36Sopenharmony_ci hstream->direction = SNDRV_PCM_STREAM_PLAYBACK; 92162306a36Sopenharmony_ci } 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci /* mem alloc for stream BDL */ 92462306a36Sopenharmony_ci ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, 92562306a36Sopenharmony_ci HDA_DSP_BDL_SIZE, &hstream->bdl); 92662306a36Sopenharmony_ci if (ret < 0) { 92762306a36Sopenharmony_ci dev_err(sdev->dev, "error: stream bdl dma alloc failed\n"); 92862306a36Sopenharmony_ci return -ENOMEM; 92962306a36Sopenharmony_ci } 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci hstream->posbuf = (__le32 *)(bus->posbuf.area + 93262306a36Sopenharmony_ci (hstream->index) * 8); 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci list_add_tail(&hstream->list, &bus->stream_list); 93562306a36Sopenharmony_ci } 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci /* store total stream count (playback + capture) from GCAP */ 93862306a36Sopenharmony_ci sof_hda->stream_max = num_total; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci return 0; 94162306a36Sopenharmony_ci} 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_civoid hda_dsp_stream_free(struct snd_sof_dev *sdev) 94462306a36Sopenharmony_ci{ 94562306a36Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 94662306a36Sopenharmony_ci struct hdac_stream *s, *_s; 94762306a36Sopenharmony_ci struct hdac_ext_stream *hext_stream; 94862306a36Sopenharmony_ci struct sof_intel_hda_stream *hda_stream; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci /* free position buffer */ 95162306a36Sopenharmony_ci if (bus->posbuf.area) 95262306a36Sopenharmony_ci snd_dma_free_pages(&bus->posbuf); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci /* free CORB/RIRB buffer - only used for HDaudio codecs */ 95562306a36Sopenharmony_ci if (bus->rb.area) 95662306a36Sopenharmony_ci snd_dma_free_pages(&bus->rb); 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci list_for_each_entry_safe(s, _s, &bus->stream_list, list) { 95962306a36Sopenharmony_ci /* TODO: decouple */ 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci /* free bdl buffer */ 96262306a36Sopenharmony_ci if (s->bdl.area) 96362306a36Sopenharmony_ci snd_dma_free_pages(&s->bdl); 96462306a36Sopenharmony_ci list_del(&s->list); 96562306a36Sopenharmony_ci hext_stream = stream_to_hdac_ext_stream(s); 96662306a36Sopenharmony_ci hda_stream = container_of(hext_stream, struct sof_intel_hda_stream, 96762306a36Sopenharmony_ci hext_stream); 96862306a36Sopenharmony_ci devm_kfree(sdev->dev, hda_stream); 96962306a36Sopenharmony_ci } 97062306a36Sopenharmony_ci} 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_cisnd_pcm_uframes_t hda_dsp_stream_get_position(struct hdac_stream *hstream, 97362306a36Sopenharmony_ci int direction, bool can_sleep) 97462306a36Sopenharmony_ci{ 97562306a36Sopenharmony_ci struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream); 97662306a36Sopenharmony_ci struct sof_intel_hda_stream *hda_stream = hstream_to_sof_hda_stream(hext_stream); 97762306a36Sopenharmony_ci struct snd_sof_dev *sdev = hda_stream->sdev; 97862306a36Sopenharmony_ci snd_pcm_uframes_t pos; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci switch (sof_hda_position_quirk) { 98162306a36Sopenharmony_ci case SOF_HDA_POSITION_QUIRK_USE_SKYLAKE_LEGACY: 98262306a36Sopenharmony_ci /* 98362306a36Sopenharmony_ci * This legacy code, inherited from the Skylake driver, 98462306a36Sopenharmony_ci * mixes DPIB registers and DPIB DDR updates and 98562306a36Sopenharmony_ci * does not seem to follow any known hardware recommendations. 98662306a36Sopenharmony_ci * It's not clear e.g. why there is a different flow 98762306a36Sopenharmony_ci * for capture and playback, the only information that matters is 98862306a36Sopenharmony_ci * what traffic class is used, and on all SOF-enabled platforms 98962306a36Sopenharmony_ci * only VC0 is supported so the work-around was likely not necessary 99062306a36Sopenharmony_ci * and quite possibly wrong. 99162306a36Sopenharmony_ci */ 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci /* DPIB/posbuf position mode: 99462306a36Sopenharmony_ci * For Playback, Use DPIB register from HDA space which 99562306a36Sopenharmony_ci * reflects the actual data transferred. 99662306a36Sopenharmony_ci * For Capture, Use the position buffer for pointer, as DPIB 99762306a36Sopenharmony_ci * is not accurate enough, its update may be completed 99862306a36Sopenharmony_ci * earlier than the data written to DDR. 99962306a36Sopenharmony_ci */ 100062306a36Sopenharmony_ci if (direction == SNDRV_PCM_STREAM_PLAYBACK) { 100162306a36Sopenharmony_ci pos = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, 100262306a36Sopenharmony_ci AZX_REG_VS_SDXDPIB_XBASE + 100362306a36Sopenharmony_ci (AZX_REG_VS_SDXDPIB_XINTERVAL * 100462306a36Sopenharmony_ci hstream->index)); 100562306a36Sopenharmony_ci } else { 100662306a36Sopenharmony_ci /* 100762306a36Sopenharmony_ci * For capture stream, we need more workaround to fix the 100862306a36Sopenharmony_ci * position incorrect issue: 100962306a36Sopenharmony_ci * 101062306a36Sopenharmony_ci * 1. Wait at least 20us before reading position buffer after 101162306a36Sopenharmony_ci * the interrupt generated(IOC), to make sure position update 101262306a36Sopenharmony_ci * happens on frame boundary i.e. 20.833uSec for 48KHz. 101362306a36Sopenharmony_ci * 2. Perform a dummy Read to DPIB register to flush DMA 101462306a36Sopenharmony_ci * position value. 101562306a36Sopenharmony_ci * 3. Read the DMA Position from posbuf. Now the readback 101662306a36Sopenharmony_ci * value should be >= period boundary. 101762306a36Sopenharmony_ci */ 101862306a36Sopenharmony_ci if (can_sleep) 101962306a36Sopenharmony_ci usleep_range(20, 21); 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, 102262306a36Sopenharmony_ci AZX_REG_VS_SDXDPIB_XBASE + 102362306a36Sopenharmony_ci (AZX_REG_VS_SDXDPIB_XINTERVAL * 102462306a36Sopenharmony_ci hstream->index)); 102562306a36Sopenharmony_ci pos = snd_hdac_stream_get_pos_posbuf(hstream); 102662306a36Sopenharmony_ci } 102762306a36Sopenharmony_ci break; 102862306a36Sopenharmony_ci case SOF_HDA_POSITION_QUIRK_USE_DPIB_REGISTERS: 102962306a36Sopenharmony_ci /* 103062306a36Sopenharmony_ci * In case VC1 traffic is disabled this is the recommended option 103162306a36Sopenharmony_ci */ 103262306a36Sopenharmony_ci pos = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, 103362306a36Sopenharmony_ci AZX_REG_VS_SDXDPIB_XBASE + 103462306a36Sopenharmony_ci (AZX_REG_VS_SDXDPIB_XINTERVAL * 103562306a36Sopenharmony_ci hstream->index)); 103662306a36Sopenharmony_ci break; 103762306a36Sopenharmony_ci case SOF_HDA_POSITION_QUIRK_USE_DPIB_DDR_UPDATE: 103862306a36Sopenharmony_ci /* 103962306a36Sopenharmony_ci * This is the recommended option when VC1 is enabled. 104062306a36Sopenharmony_ci * While this isn't needed for SOF platforms it's added for 104162306a36Sopenharmony_ci * consistency and debug. 104262306a36Sopenharmony_ci */ 104362306a36Sopenharmony_ci pos = snd_hdac_stream_get_pos_posbuf(hstream); 104462306a36Sopenharmony_ci break; 104562306a36Sopenharmony_ci default: 104662306a36Sopenharmony_ci dev_err_once(sdev->dev, "hda_position_quirk value %d not supported\n", 104762306a36Sopenharmony_ci sof_hda_position_quirk); 104862306a36Sopenharmony_ci pos = 0; 104962306a36Sopenharmony_ci break; 105062306a36Sopenharmony_ci } 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci if (pos >= hstream->bufsize) 105362306a36Sopenharmony_ci pos = 0; 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci return pos; 105662306a36Sopenharmony_ci} 1057