18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// This file is provided under a dual BSD/GPLv2 license. When using or 48c2ecf20Sopenharmony_ci// redistributing this file, you may do so under either license. 58c2ecf20Sopenharmony_ci// 68c2ecf20Sopenharmony_ci// Copyright(c) 2018 Intel Corporation. All rights reserved. 78c2ecf20Sopenharmony_ci// 88c2ecf20Sopenharmony_ci// Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com> 98c2ecf20Sopenharmony_ci// Ranjani Sridharan <ranjani.sridharan@linux.intel.com> 108c2ecf20Sopenharmony_ci// Rander Wang <rander.wang@intel.com> 118c2ecf20Sopenharmony_ci// Keyon Jie <yang.jie@linux.intel.com> 128c2ecf20Sopenharmony_ci// 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci/* 158c2ecf20Sopenharmony_ci * Hardware interface for generic Intel audio DSP HDA IP 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 198c2ecf20Sopenharmony_ci#include <sound/hdaudio_ext.h> 208c2ecf20Sopenharmony_ci#include <sound/hda_register.h> 218c2ecf20Sopenharmony_ci#include <sound/sof.h> 228c2ecf20Sopenharmony_ci#include "../ops.h" 238c2ecf20Sopenharmony_ci#include "../sof-audio.h" 248c2ecf20Sopenharmony_ci#include "hda.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define HDA_LTRP_GB_VALUE_US 95 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* 298c2ecf20Sopenharmony_ci * set up one of BDL entries for a stream 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_cistatic int hda_setup_bdle(struct snd_sof_dev *sdev, 328c2ecf20Sopenharmony_ci struct snd_dma_buffer *dmab, 338c2ecf20Sopenharmony_ci struct hdac_stream *stream, 348c2ecf20Sopenharmony_ci struct sof_intel_dsp_bdl **bdlp, 358c2ecf20Sopenharmony_ci int offset, int size, int ioc) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 388c2ecf20Sopenharmony_ci struct sof_intel_dsp_bdl *bdl = *bdlp; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci while (size > 0) { 418c2ecf20Sopenharmony_ci dma_addr_t addr; 428c2ecf20Sopenharmony_ci int chunk; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci if (stream->frags >= HDA_DSP_MAX_BDL_ENTRIES) { 458c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: stream frags exceeded\n"); 468c2ecf20Sopenharmony_ci return -EINVAL; 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci addr = snd_sgbuf_get_addr(dmab, offset); 508c2ecf20Sopenharmony_ci /* program BDL addr */ 518c2ecf20Sopenharmony_ci bdl->addr_l = cpu_to_le32(lower_32_bits(addr)); 528c2ecf20Sopenharmony_ci bdl->addr_h = cpu_to_le32(upper_32_bits(addr)); 538c2ecf20Sopenharmony_ci /* program BDL size */ 548c2ecf20Sopenharmony_ci chunk = snd_sgbuf_get_chunk_size(dmab, offset, size); 558c2ecf20Sopenharmony_ci /* one BDLE should not cross 4K boundary */ 568c2ecf20Sopenharmony_ci if (bus->align_bdle_4k) { 578c2ecf20Sopenharmony_ci u32 remain = 0x1000 - (offset & 0xfff); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci if (chunk > remain) 608c2ecf20Sopenharmony_ci chunk = remain; 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci bdl->size = cpu_to_le32(chunk); 638c2ecf20Sopenharmony_ci /* only program IOC when the whole segment is processed */ 648c2ecf20Sopenharmony_ci size -= chunk; 658c2ecf20Sopenharmony_ci bdl->ioc = (size || !ioc) ? 0 : cpu_to_le32(0x01); 668c2ecf20Sopenharmony_ci bdl++; 678c2ecf20Sopenharmony_ci stream->frags++; 688c2ecf20Sopenharmony_ci offset += chunk; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci dev_vdbg(sdev->dev, "bdl, frags:%d, chunk size:0x%x;\n", 718c2ecf20Sopenharmony_ci stream->frags, chunk); 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci *bdlp = bdl; 758c2ecf20Sopenharmony_ci return offset; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* 798c2ecf20Sopenharmony_ci * set up Buffer Descriptor List (BDL) for host memory transfer 808c2ecf20Sopenharmony_ci * BDL describes the location of the individual buffers and is little endian. 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_ciint hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev, 838c2ecf20Sopenharmony_ci struct snd_dma_buffer *dmab, 848c2ecf20Sopenharmony_ci struct hdac_stream *stream) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; 878c2ecf20Sopenharmony_ci struct sof_intel_dsp_bdl *bdl; 888c2ecf20Sopenharmony_ci int i, offset, period_bytes, periods; 898c2ecf20Sopenharmony_ci int remain, ioc; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci period_bytes = stream->period_bytes; 928c2ecf20Sopenharmony_ci dev_dbg(sdev->dev, "period_bytes:0x%x\n", period_bytes); 938c2ecf20Sopenharmony_ci if (!period_bytes) 948c2ecf20Sopenharmony_ci period_bytes = stream->bufsize; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci periods = stream->bufsize / period_bytes; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci dev_dbg(sdev->dev, "periods:%d\n", periods); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci remain = stream->bufsize % period_bytes; 1018c2ecf20Sopenharmony_ci if (remain) 1028c2ecf20Sopenharmony_ci periods++; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci /* program the initial BDL entries */ 1058c2ecf20Sopenharmony_ci bdl = (struct sof_intel_dsp_bdl *)stream->bdl.area; 1068c2ecf20Sopenharmony_ci offset = 0; 1078c2ecf20Sopenharmony_ci stream->frags = 0; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* 1108c2ecf20Sopenharmony_ci * set IOC if don't use position IPC 1118c2ecf20Sopenharmony_ci * and period_wakeup needed. 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_ci ioc = hda->no_ipc_position ? 1148c2ecf20Sopenharmony_ci !stream->no_period_wakeup : 0; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci for (i = 0; i < periods; i++) { 1178c2ecf20Sopenharmony_ci if (i == (periods - 1) && remain) 1188c2ecf20Sopenharmony_ci /* set the last small entry */ 1198c2ecf20Sopenharmony_ci offset = hda_setup_bdle(sdev, dmab, 1208c2ecf20Sopenharmony_ci stream, &bdl, offset, 1218c2ecf20Sopenharmony_ci remain, 0); 1228c2ecf20Sopenharmony_ci else 1238c2ecf20Sopenharmony_ci offset = hda_setup_bdle(sdev, dmab, 1248c2ecf20Sopenharmony_ci stream, &bdl, offset, 1258c2ecf20Sopenharmony_ci period_bytes, ioc); 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci return offset; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ciint hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, 1328c2ecf20Sopenharmony_ci struct hdac_ext_stream *stream, 1338c2ecf20Sopenharmony_ci int enable, u32 size) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct hdac_stream *hstream = &stream->hstream; 1368c2ecf20Sopenharmony_ci u32 mask; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (!sdev->bar[HDA_DSP_SPIB_BAR]) { 1398c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: address of spib capability is NULL\n"); 1408c2ecf20Sopenharmony_ci return -EINVAL; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci mask = (1 << hstream->index); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* enable/disable SPIB for the stream */ 1468c2ecf20Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_SPIB_BAR, 1478c2ecf20Sopenharmony_ci SOF_HDA_ADSP_REG_CL_SPBFIFO_SPBFCCTL, mask, 1488c2ecf20Sopenharmony_ci enable << hstream->index); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* set the SPIB value */ 1518c2ecf20Sopenharmony_ci sof_io_write(sdev, stream->spib_addr, size); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci return 0; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci/* get next unused stream */ 1578c2ecf20Sopenharmony_cistruct hdac_ext_stream * 1588c2ecf20Sopenharmony_cihda_dsp_stream_get(struct snd_sof_dev *sdev, int direction) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 1618c2ecf20Sopenharmony_ci struct sof_intel_hda_stream *hda_stream; 1628c2ecf20Sopenharmony_ci struct hdac_ext_stream *stream = NULL; 1638c2ecf20Sopenharmony_ci struct hdac_stream *s; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci spin_lock_irq(&bus->reg_lock); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* get an unused stream */ 1688c2ecf20Sopenharmony_ci list_for_each_entry(s, &bus->stream_list, list) { 1698c2ecf20Sopenharmony_ci if (s->direction == direction && !s->opened) { 1708c2ecf20Sopenharmony_ci stream = stream_to_hdac_ext_stream(s); 1718c2ecf20Sopenharmony_ci hda_stream = container_of(stream, 1728c2ecf20Sopenharmony_ci struct sof_intel_hda_stream, 1738c2ecf20Sopenharmony_ci hda_stream); 1748c2ecf20Sopenharmony_ci /* check if the host DMA channel is reserved */ 1758c2ecf20Sopenharmony_ci if (hda_stream->host_reserved) 1768c2ecf20Sopenharmony_ci continue; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci s->opened = true; 1798c2ecf20Sopenharmony_ci break; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci spin_unlock_irq(&bus->reg_lock); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* stream found ? */ 1868c2ecf20Sopenharmony_ci if (!stream) 1878c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: no free %s streams\n", 1888c2ecf20Sopenharmony_ci direction == SNDRV_PCM_STREAM_PLAYBACK ? 1898c2ecf20Sopenharmony_ci "playback" : "capture"); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* 1928c2ecf20Sopenharmony_ci * Disable DMI Link L1 entry when capture stream is opened. 1938c2ecf20Sopenharmony_ci * Workaround to address a known issue with host DMA that results 1948c2ecf20Sopenharmony_ci * in xruns during pause/release in capture scenarios. 1958c2ecf20Sopenharmony_ci */ 1968c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_ALWAYS_ENABLE_DMI_L1)) 1978c2ecf20Sopenharmony_ci if (stream && direction == SNDRV_PCM_STREAM_CAPTURE) 1988c2ecf20Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, 1998c2ecf20Sopenharmony_ci HDA_VS_INTEL_EM2, 2008c2ecf20Sopenharmony_ci HDA_VS_INTEL_EM2_L1SEN, 0); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci return stream; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci/* free a stream */ 2068c2ecf20Sopenharmony_ciint hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 2098c2ecf20Sopenharmony_ci struct hdac_stream *s; 2108c2ecf20Sopenharmony_ci bool active_capture_stream = false; 2118c2ecf20Sopenharmony_ci bool found = false; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci spin_lock_irq(&bus->reg_lock); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci /* 2168c2ecf20Sopenharmony_ci * close stream matching the stream tag 2178c2ecf20Sopenharmony_ci * and check if there are any open capture streams. 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_ci list_for_each_entry(s, &bus->stream_list, list) { 2208c2ecf20Sopenharmony_ci if (!s->opened) 2218c2ecf20Sopenharmony_ci continue; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (s->direction == direction && s->stream_tag == stream_tag) { 2248c2ecf20Sopenharmony_ci s->opened = false; 2258c2ecf20Sopenharmony_ci found = true; 2268c2ecf20Sopenharmony_ci } else if (s->direction == SNDRV_PCM_STREAM_CAPTURE) { 2278c2ecf20Sopenharmony_ci active_capture_stream = true; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci spin_unlock_irq(&bus->reg_lock); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci /* Enable DMI L1 entry if there are no capture streams open */ 2348c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_ALWAYS_ENABLE_DMI_L1)) 2358c2ecf20Sopenharmony_ci if (!active_capture_stream) 2368c2ecf20Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, 2378c2ecf20Sopenharmony_ci HDA_VS_INTEL_EM2, 2388c2ecf20Sopenharmony_ci HDA_VS_INTEL_EM2_L1SEN, 2398c2ecf20Sopenharmony_ci HDA_VS_INTEL_EM2_L1SEN); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (!found) { 2428c2ecf20Sopenharmony_ci dev_dbg(sdev->dev, "stream_tag %d not opened!\n", stream_tag); 2438c2ecf20Sopenharmony_ci return -ENODEV; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci return 0; 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ciint hda_dsp_stream_trigger(struct snd_sof_dev *sdev, 2508c2ecf20Sopenharmony_ci struct hdac_ext_stream *stream, int cmd) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci struct hdac_stream *hstream = &stream->hstream; 2538c2ecf20Sopenharmony_ci int sd_offset = SOF_STREAM_SD_OFFSET(hstream); 2548c2ecf20Sopenharmony_ci u32 dma_start = SOF_HDA_SD_CTL_DMA_START; 2558c2ecf20Sopenharmony_ci int ret; 2568c2ecf20Sopenharmony_ci u32 run; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci /* cmd must be for audio stream */ 2598c2ecf20Sopenharmony_ci switch (cmd) { 2608c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 2618c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 2628c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 2638c2ecf20Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, 2648c2ecf20Sopenharmony_ci 1 << hstream->index, 2658c2ecf20Sopenharmony_ci 1 << hstream->index); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, 2688c2ecf20Sopenharmony_ci sd_offset, 2698c2ecf20Sopenharmony_ci SOF_HDA_SD_CTL_DMA_START | 2708c2ecf20Sopenharmony_ci SOF_HDA_CL_DMA_SD_INT_MASK, 2718c2ecf20Sopenharmony_ci SOF_HDA_SD_CTL_DMA_START | 2728c2ecf20Sopenharmony_ci SOF_HDA_CL_DMA_SD_INT_MASK); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci ret = snd_sof_dsp_read_poll_timeout(sdev, 2758c2ecf20Sopenharmony_ci HDA_DSP_HDA_BAR, 2768c2ecf20Sopenharmony_ci sd_offset, run, 2778c2ecf20Sopenharmony_ci ((run & dma_start) == dma_start), 2788c2ecf20Sopenharmony_ci HDA_DSP_REG_POLL_INTERVAL_US, 2798c2ecf20Sopenharmony_ci HDA_DSP_STREAM_RUN_TIMEOUT); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (ret < 0) { 2828c2ecf20Sopenharmony_ci dev_err(sdev->dev, 2838c2ecf20Sopenharmony_ci "error: %s: cmd %d: timeout on STREAM_SD_OFFSET read\n", 2848c2ecf20Sopenharmony_ci __func__, cmd); 2858c2ecf20Sopenharmony_ci return ret; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci hstream->running = true; 2898c2ecf20Sopenharmony_ci break; 2908c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 2918c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 2928c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 2938c2ecf20Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, 2948c2ecf20Sopenharmony_ci sd_offset, 2958c2ecf20Sopenharmony_ci SOF_HDA_SD_CTL_DMA_START | 2968c2ecf20Sopenharmony_ci SOF_HDA_CL_DMA_SD_INT_MASK, 0x0); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_HDA_BAR, 2998c2ecf20Sopenharmony_ci sd_offset, run, 3008c2ecf20Sopenharmony_ci !(run & dma_start), 3018c2ecf20Sopenharmony_ci HDA_DSP_REG_POLL_INTERVAL_US, 3028c2ecf20Sopenharmony_ci HDA_DSP_STREAM_RUN_TIMEOUT); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (ret < 0) { 3058c2ecf20Sopenharmony_ci dev_err(sdev->dev, 3068c2ecf20Sopenharmony_ci "error: %s: cmd %d: timeout on STREAM_SD_OFFSET read\n", 3078c2ecf20Sopenharmony_ci __func__, cmd); 3088c2ecf20Sopenharmony_ci return ret; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, sd_offset + 3128c2ecf20Sopenharmony_ci SOF_HDA_ADSP_REG_CL_SD_STS, 3138c2ecf20Sopenharmony_ci SOF_HDA_CL_DMA_SD_INT_MASK); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci hstream->running = false; 3168c2ecf20Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, 3178c2ecf20Sopenharmony_ci 1 << hstream->index, 0x0); 3188c2ecf20Sopenharmony_ci break; 3198c2ecf20Sopenharmony_ci default: 3208c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: unknown command: %d\n", cmd); 3218c2ecf20Sopenharmony_ci return -EINVAL; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci return 0; 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci/* minimal recommended programming for ICCMAX stream */ 3288c2ecf20Sopenharmony_ciint hda_dsp_iccmax_stream_hw_params(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream, 3298c2ecf20Sopenharmony_ci struct snd_dma_buffer *dmab, 3308c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 3338c2ecf20Sopenharmony_ci struct hdac_stream *hstream = &stream->hstream; 3348c2ecf20Sopenharmony_ci int sd_offset = SOF_STREAM_SD_OFFSET(hstream); 3358c2ecf20Sopenharmony_ci int ret; 3368c2ecf20Sopenharmony_ci u32 mask = 0x1 << hstream->index; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (!stream) { 3398c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: no stream available\n"); 3408c2ecf20Sopenharmony_ci return -ENODEV; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (hstream->posbuf) 3448c2ecf20Sopenharmony_ci *hstream->posbuf = 0; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* reset BDL address */ 3478c2ecf20Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, 3488c2ecf20Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPL, 3498c2ecf20Sopenharmony_ci 0x0); 3508c2ecf20Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, 3518c2ecf20Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, 3528c2ecf20Sopenharmony_ci 0x0); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci hstream->frags = 0; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci ret = hda_dsp_stream_setup_bdl(sdev, dmab, hstream); 3578c2ecf20Sopenharmony_ci if (ret < 0) { 3588c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: set up of BDL failed\n"); 3598c2ecf20Sopenharmony_ci return ret; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci /* program BDL address */ 3638c2ecf20Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, 3648c2ecf20Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPL, 3658c2ecf20Sopenharmony_ci (u32)hstream->bdl.addr); 3668c2ecf20Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, 3678c2ecf20Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, 3688c2ecf20Sopenharmony_ci upper_32_bits(hstream->bdl.addr)); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci /* program cyclic buffer length */ 3718c2ecf20Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, 3728c2ecf20Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_CL_SD_CBL, 3738c2ecf20Sopenharmony_ci hstream->bufsize); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci /* program last valid index */ 3768c2ecf20Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, 3778c2ecf20Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_CL_SD_LVI, 3788c2ecf20Sopenharmony_ci 0xffff, (hstream->frags - 1)); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* decouple host and link DMA, enable DSP features */ 3818c2ecf20Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, 3828c2ecf20Sopenharmony_ci mask, mask); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci /* Follow HW recommendation to set the guardband value to 95us during FW boot */ 3858c2ecf20Sopenharmony_ci snd_hdac_chip_updateb(bus, VS_LTRP, HDA_VS_INTEL_LTRP_GB_MASK, HDA_LTRP_GB_VALUE_US); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci /* start DMA */ 3888c2ecf20Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 3898c2ecf20Sopenharmony_ci SOF_HDA_SD_CTL_DMA_START, SOF_HDA_SD_CTL_DMA_START); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci return 0; 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci/* 3958c2ecf20Sopenharmony_ci * prepare for common hdac registers settings, for both code loader 3968c2ecf20Sopenharmony_ci * and normal stream. 3978c2ecf20Sopenharmony_ci */ 3988c2ecf20Sopenharmony_ciint hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, 3998c2ecf20Sopenharmony_ci struct hdac_ext_stream *stream, 4008c2ecf20Sopenharmony_ci struct snd_dma_buffer *dmab, 4018c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 4048c2ecf20Sopenharmony_ci struct hdac_stream *hstream = &stream->hstream; 4058c2ecf20Sopenharmony_ci int sd_offset = SOF_STREAM_SD_OFFSET(hstream); 4068c2ecf20Sopenharmony_ci int ret, timeout = HDA_DSP_STREAM_RESET_TIMEOUT; 4078c2ecf20Sopenharmony_ci u32 dma_start = SOF_HDA_SD_CTL_DMA_START; 4088c2ecf20Sopenharmony_ci u32 val, mask; 4098c2ecf20Sopenharmony_ci u32 run; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci if (!stream) { 4128c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: no stream available\n"); 4138c2ecf20Sopenharmony_ci return -ENODEV; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci /* decouple host and link DMA */ 4178c2ecf20Sopenharmony_ci mask = 0x1 << hstream->index; 4188c2ecf20Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, 4198c2ecf20Sopenharmony_ci mask, mask); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci if (!dmab) { 4228c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: no dma buffer allocated!\n"); 4238c2ecf20Sopenharmony_ci return -ENODEV; 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci /* clear stream status */ 4278c2ecf20Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 4288c2ecf20Sopenharmony_ci SOF_HDA_CL_DMA_SD_INT_MASK | 4298c2ecf20Sopenharmony_ci SOF_HDA_SD_CTL_DMA_START, 0); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_HDA_BAR, 4328c2ecf20Sopenharmony_ci sd_offset, run, 4338c2ecf20Sopenharmony_ci !(run & dma_start), 4348c2ecf20Sopenharmony_ci HDA_DSP_REG_POLL_INTERVAL_US, 4358c2ecf20Sopenharmony_ci HDA_DSP_STREAM_RUN_TIMEOUT); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (ret < 0) { 4388c2ecf20Sopenharmony_ci dev_err(sdev->dev, 4398c2ecf20Sopenharmony_ci "error: %s: timeout on STREAM_SD_OFFSET read1\n", 4408c2ecf20Sopenharmony_ci __func__); 4418c2ecf20Sopenharmony_ci return ret; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, 4458c2ecf20Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS, 4468c2ecf20Sopenharmony_ci SOF_HDA_CL_DMA_SD_INT_MASK, 4478c2ecf20Sopenharmony_ci SOF_HDA_CL_DMA_SD_INT_MASK); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci /* stream reset */ 4508c2ecf20Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 0x1, 4518c2ecf20Sopenharmony_ci 0x1); 4528c2ecf20Sopenharmony_ci udelay(3); 4538c2ecf20Sopenharmony_ci do { 4548c2ecf20Sopenharmony_ci val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, 4558c2ecf20Sopenharmony_ci sd_offset); 4568c2ecf20Sopenharmony_ci if (val & 0x1) 4578c2ecf20Sopenharmony_ci break; 4588c2ecf20Sopenharmony_ci } while (--timeout); 4598c2ecf20Sopenharmony_ci if (timeout == 0) { 4608c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: stream reset failed\n"); 4618c2ecf20Sopenharmony_ci return -ETIMEDOUT; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci timeout = HDA_DSP_STREAM_RESET_TIMEOUT; 4658c2ecf20Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 0x1, 4668c2ecf20Sopenharmony_ci 0x0); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci /* wait for hardware to report that stream is out of reset */ 4698c2ecf20Sopenharmony_ci udelay(3); 4708c2ecf20Sopenharmony_ci do { 4718c2ecf20Sopenharmony_ci val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, 4728c2ecf20Sopenharmony_ci sd_offset); 4738c2ecf20Sopenharmony_ci if ((val & 0x1) == 0) 4748c2ecf20Sopenharmony_ci break; 4758c2ecf20Sopenharmony_ci } while (--timeout); 4768c2ecf20Sopenharmony_ci if (timeout == 0) { 4778c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: timeout waiting for stream reset\n"); 4788c2ecf20Sopenharmony_ci return -ETIMEDOUT; 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci if (hstream->posbuf) 4828c2ecf20Sopenharmony_ci *hstream->posbuf = 0; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci /* reset BDL address */ 4858c2ecf20Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, 4868c2ecf20Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPL, 4878c2ecf20Sopenharmony_ci 0x0); 4888c2ecf20Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, 4898c2ecf20Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, 4908c2ecf20Sopenharmony_ci 0x0); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci /* clear stream status */ 4938c2ecf20Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 4948c2ecf20Sopenharmony_ci SOF_HDA_CL_DMA_SD_INT_MASK | 4958c2ecf20Sopenharmony_ci SOF_HDA_SD_CTL_DMA_START, 0); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_HDA_BAR, 4988c2ecf20Sopenharmony_ci sd_offset, run, 4998c2ecf20Sopenharmony_ci !(run & dma_start), 5008c2ecf20Sopenharmony_ci HDA_DSP_REG_POLL_INTERVAL_US, 5018c2ecf20Sopenharmony_ci HDA_DSP_STREAM_RUN_TIMEOUT); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci if (ret < 0) { 5048c2ecf20Sopenharmony_ci dev_err(sdev->dev, 5058c2ecf20Sopenharmony_ci "error: %s: timeout on STREAM_SD_OFFSET read2\n", 5068c2ecf20Sopenharmony_ci __func__); 5078c2ecf20Sopenharmony_ci return ret; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, 5118c2ecf20Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS, 5128c2ecf20Sopenharmony_ci SOF_HDA_CL_DMA_SD_INT_MASK, 5138c2ecf20Sopenharmony_ci SOF_HDA_CL_DMA_SD_INT_MASK); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci hstream->frags = 0; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci ret = hda_dsp_stream_setup_bdl(sdev, dmab, hstream); 5188c2ecf20Sopenharmony_ci if (ret < 0) { 5198c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: set up of BDL failed\n"); 5208c2ecf20Sopenharmony_ci return ret; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci /* program stream tag to set up stream descriptor for DMA */ 5248c2ecf20Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 5258c2ecf20Sopenharmony_ci SOF_HDA_CL_SD_CTL_STREAM_TAG_MASK, 5268c2ecf20Sopenharmony_ci hstream->stream_tag << 5278c2ecf20Sopenharmony_ci SOF_HDA_CL_SD_CTL_STREAM_TAG_SHIFT); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci /* program cyclic buffer length */ 5308c2ecf20Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, 5318c2ecf20Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_CL_SD_CBL, 5328c2ecf20Sopenharmony_ci hstream->bufsize); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci /* 5358c2ecf20Sopenharmony_ci * Recommended hardware programming sequence for HDAudio DMA format 5368c2ecf20Sopenharmony_ci * 5378c2ecf20Sopenharmony_ci * 1. Put DMA into coupled mode by clearing PPCTL.PROCEN bit 5388c2ecf20Sopenharmony_ci * for corresponding stream index before the time of writing 5398c2ecf20Sopenharmony_ci * format to SDxFMT register. 5408c2ecf20Sopenharmony_ci * 2. Write SDxFMT 5418c2ecf20Sopenharmony_ci * 3. Set PPCTL.PROCEN bit for corresponding stream index to 5428c2ecf20Sopenharmony_ci * enable decoupled mode 5438c2ecf20Sopenharmony_ci */ 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci /* couple host and link DMA, disable DSP features */ 5468c2ecf20Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, 5478c2ecf20Sopenharmony_ci mask, 0); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci /* program stream format */ 5508c2ecf20Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, 5518c2ecf20Sopenharmony_ci sd_offset + 5528c2ecf20Sopenharmony_ci SOF_HDA_ADSP_REG_CL_SD_FORMAT, 5538c2ecf20Sopenharmony_ci 0xffff, hstream->format_val); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci /* decouple host and link DMA, enable DSP features */ 5568c2ecf20Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, 5578c2ecf20Sopenharmony_ci mask, mask); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci /* program last valid index */ 5608c2ecf20Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, 5618c2ecf20Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_CL_SD_LVI, 5628c2ecf20Sopenharmony_ci 0xffff, (hstream->frags - 1)); 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci /* program BDL address */ 5658c2ecf20Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, 5668c2ecf20Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPL, 5678c2ecf20Sopenharmony_ci (u32)hstream->bdl.addr); 5688c2ecf20Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, 5698c2ecf20Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, 5708c2ecf20Sopenharmony_ci upper_32_bits(hstream->bdl.addr)); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci /* enable position buffer */ 5738c2ecf20Sopenharmony_ci if (!(snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE) 5748c2ecf20Sopenharmony_ci & SOF_HDA_ADSP_DPLBASE_ENABLE)) { 5758c2ecf20Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPUBASE, 5768c2ecf20Sopenharmony_ci upper_32_bits(bus->posbuf.addr)); 5778c2ecf20Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE, 5788c2ecf20Sopenharmony_ci (u32)bus->posbuf.addr | 5798c2ecf20Sopenharmony_ci SOF_HDA_ADSP_DPLBASE_ENABLE); 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci /* set interrupt enable bits */ 5838c2ecf20Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 5848c2ecf20Sopenharmony_ci SOF_HDA_CL_DMA_SD_INT_MASK, 5858c2ecf20Sopenharmony_ci SOF_HDA_CL_DMA_SD_INT_MASK); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci /* read FIFO size */ 5888c2ecf20Sopenharmony_ci if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK) { 5898c2ecf20Sopenharmony_ci hstream->fifo_size = 5908c2ecf20Sopenharmony_ci snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, 5918c2ecf20Sopenharmony_ci sd_offset + 5928c2ecf20Sopenharmony_ci SOF_HDA_ADSP_REG_CL_SD_FIFOSIZE); 5938c2ecf20Sopenharmony_ci hstream->fifo_size &= 0xffff; 5948c2ecf20Sopenharmony_ci hstream->fifo_size += 1; 5958c2ecf20Sopenharmony_ci } else { 5968c2ecf20Sopenharmony_ci hstream->fifo_size = 0; 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci return ret; 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ciint hda_dsp_stream_hw_free(struct snd_sof_dev *sdev, 6038c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci struct hdac_stream *stream = substream->runtime->private_data; 6068c2ecf20Sopenharmony_ci struct hdac_ext_stream *link_dev = container_of(stream, 6078c2ecf20Sopenharmony_ci struct hdac_ext_stream, 6088c2ecf20Sopenharmony_ci hstream); 6098c2ecf20Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 6108c2ecf20Sopenharmony_ci u32 mask = 0x1 << stream->index; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci spin_lock_irq(&bus->reg_lock); 6138c2ecf20Sopenharmony_ci /* couple host and link DMA if link DMA channel is idle */ 6148c2ecf20Sopenharmony_ci if (!link_dev->link_locked) 6158c2ecf20Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, 6168c2ecf20Sopenharmony_ci SOF_HDA_REG_PP_PPCTL, mask, 0); 6178c2ecf20Sopenharmony_ci spin_unlock_irq(&bus->reg_lock); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci stream->substream = NULL; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci return 0; 6228c2ecf20Sopenharmony_ci} 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_cibool hda_dsp_check_stream_irq(struct snd_sof_dev *sdev) 6258c2ecf20Sopenharmony_ci{ 6268c2ecf20Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 6278c2ecf20Sopenharmony_ci bool ret = false; 6288c2ecf20Sopenharmony_ci u32 status; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci /* The function can be called at irq thread, so use spin_lock_irq */ 6318c2ecf20Sopenharmony_ci spin_lock_irq(&bus->reg_lock); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci status = snd_hdac_chip_readl(bus, INTSTS); 6348c2ecf20Sopenharmony_ci dev_vdbg(bus->dev, "stream irq, INTSTS status: 0x%x\n", status); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci /* if Register inaccessible, ignore it.*/ 6378c2ecf20Sopenharmony_ci if (status != 0xffffffff) 6388c2ecf20Sopenharmony_ci ret = true; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci spin_unlock_irq(&bus->reg_lock); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci return ret; 6438c2ecf20Sopenharmony_ci} 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_cistatic void 6468c2ecf20Sopenharmony_cihda_dsp_set_bytes_transferred(struct hdac_stream *hstream, u64 buffer_size) 6478c2ecf20Sopenharmony_ci{ 6488c2ecf20Sopenharmony_ci u64 prev_pos, pos, num_bytes; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci div64_u64_rem(hstream->curr_pos, buffer_size, &prev_pos); 6518c2ecf20Sopenharmony_ci pos = snd_hdac_stream_get_pos_posbuf(hstream); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci if (pos < prev_pos) 6548c2ecf20Sopenharmony_ci num_bytes = (buffer_size - prev_pos) + pos; 6558c2ecf20Sopenharmony_ci else 6568c2ecf20Sopenharmony_ci num_bytes = pos - prev_pos; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci hstream->curr_pos += num_bytes; 6598c2ecf20Sopenharmony_ci} 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_cistatic bool hda_dsp_stream_check(struct hdac_bus *bus, u32 status) 6628c2ecf20Sopenharmony_ci{ 6638c2ecf20Sopenharmony_ci struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus); 6648c2ecf20Sopenharmony_ci struct hdac_stream *s; 6658c2ecf20Sopenharmony_ci bool active = false; 6668c2ecf20Sopenharmony_ci u32 sd_status; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci list_for_each_entry(s, &bus->stream_list, list) { 6698c2ecf20Sopenharmony_ci if (status & BIT(s->index) && s->opened) { 6708c2ecf20Sopenharmony_ci sd_status = snd_hdac_stream_readb(s, SD_STS); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci dev_vdbg(bus->dev, "stream %d status 0x%x\n", 6738c2ecf20Sopenharmony_ci s->index, sd_status); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci snd_hdac_stream_writeb(s, SD_STS, sd_status); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci active = true; 6788c2ecf20Sopenharmony_ci if ((!s->substream && !s->cstream) || 6798c2ecf20Sopenharmony_ci !s->running || 6808c2ecf20Sopenharmony_ci (sd_status & SOF_HDA_CL_DMA_SD_INT_COMPLETE) == 0) 6818c2ecf20Sopenharmony_ci continue; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci /* Inform ALSA only in case not do that with IPC */ 6848c2ecf20Sopenharmony_ci if (s->substream && sof_hda->no_ipc_position) { 6858c2ecf20Sopenharmony_ci snd_sof_pcm_period_elapsed(s->substream); 6868c2ecf20Sopenharmony_ci } else if (s->cstream) { 6878c2ecf20Sopenharmony_ci hda_dsp_set_bytes_transferred(s, 6888c2ecf20Sopenharmony_ci s->cstream->runtime->buffer_size); 6898c2ecf20Sopenharmony_ci snd_compr_fragment_elapsed(s->cstream); 6908c2ecf20Sopenharmony_ci } 6918c2ecf20Sopenharmony_ci } 6928c2ecf20Sopenharmony_ci } 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci return active; 6958c2ecf20Sopenharmony_ci} 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ciirqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context) 6988c2ecf20Sopenharmony_ci{ 6998c2ecf20Sopenharmony_ci struct snd_sof_dev *sdev = context; 7008c2ecf20Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 7018c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) 7028c2ecf20Sopenharmony_ci u32 rirb_status; 7038c2ecf20Sopenharmony_ci#endif 7048c2ecf20Sopenharmony_ci bool active; 7058c2ecf20Sopenharmony_ci u32 status; 7068c2ecf20Sopenharmony_ci int i; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci /* 7098c2ecf20Sopenharmony_ci * Loop 10 times to handle missed interrupts caused by 7108c2ecf20Sopenharmony_ci * unsolicited responses from the codec 7118c2ecf20Sopenharmony_ci */ 7128c2ecf20Sopenharmony_ci for (i = 0, active = true; i < 10 && active; i++) { 7138c2ecf20Sopenharmony_ci spin_lock_irq(&bus->reg_lock); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci status = snd_hdac_chip_readl(bus, INTSTS); 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci /* check streams */ 7188c2ecf20Sopenharmony_ci active = hda_dsp_stream_check(bus, status); 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci /* check and clear RIRB interrupt */ 7218c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) 7228c2ecf20Sopenharmony_ci if (status & AZX_INT_CTRL_EN) { 7238c2ecf20Sopenharmony_ci rirb_status = snd_hdac_chip_readb(bus, RIRBSTS); 7248c2ecf20Sopenharmony_ci if (rirb_status & RIRB_INT_MASK) { 7258c2ecf20Sopenharmony_ci /* 7268c2ecf20Sopenharmony_ci * Clearing the interrupt status here ensures 7278c2ecf20Sopenharmony_ci * that no interrupt gets masked after the RIRB 7288c2ecf20Sopenharmony_ci * wp is read in snd_hdac_bus_update_rirb. 7298c2ecf20Sopenharmony_ci */ 7308c2ecf20Sopenharmony_ci snd_hdac_chip_writeb(bus, RIRBSTS, 7318c2ecf20Sopenharmony_ci RIRB_INT_MASK); 7328c2ecf20Sopenharmony_ci active = true; 7338c2ecf20Sopenharmony_ci if (rirb_status & RIRB_INT_RESPONSE) 7348c2ecf20Sopenharmony_ci snd_hdac_bus_update_rirb(bus); 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_ci } 7378c2ecf20Sopenharmony_ci#endif 7388c2ecf20Sopenharmony_ci spin_unlock_irq(&bus->reg_lock); 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci return IRQ_HANDLED; 7428c2ecf20Sopenharmony_ci} 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ciint hda_dsp_stream_init(struct snd_sof_dev *sdev) 7458c2ecf20Sopenharmony_ci{ 7468c2ecf20Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 7478c2ecf20Sopenharmony_ci struct hdac_ext_stream *stream; 7488c2ecf20Sopenharmony_ci struct hdac_stream *hstream; 7498c2ecf20Sopenharmony_ci struct pci_dev *pci = to_pci_dev(sdev->dev); 7508c2ecf20Sopenharmony_ci struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus); 7518c2ecf20Sopenharmony_ci int sd_offset; 7528c2ecf20Sopenharmony_ci int i, num_playback, num_capture, num_total, ret; 7538c2ecf20Sopenharmony_ci u32 gcap; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci gcap = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_GCAP); 7568c2ecf20Sopenharmony_ci dev_dbg(sdev->dev, "hda global caps = 0x%x\n", gcap); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci /* get stream count from GCAP */ 7598c2ecf20Sopenharmony_ci num_capture = (gcap >> 8) & 0x0f; 7608c2ecf20Sopenharmony_ci num_playback = (gcap >> 12) & 0x0f; 7618c2ecf20Sopenharmony_ci num_total = num_playback + num_capture; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci dev_dbg(sdev->dev, "detected %d playback and %d capture streams\n", 7648c2ecf20Sopenharmony_ci num_playback, num_capture); 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci if (num_playback >= SOF_HDA_PLAYBACK_STREAMS) { 7678c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: too many playback streams %d\n", 7688c2ecf20Sopenharmony_ci num_playback); 7698c2ecf20Sopenharmony_ci return -EINVAL; 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci if (num_capture >= SOF_HDA_CAPTURE_STREAMS) { 7738c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: too many capture streams %d\n", 7748c2ecf20Sopenharmony_ci num_playback); 7758c2ecf20Sopenharmony_ci return -EINVAL; 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci /* 7798c2ecf20Sopenharmony_ci * mem alloc for the position buffer 7808c2ecf20Sopenharmony_ci * TODO: check position buffer update 7818c2ecf20Sopenharmony_ci */ 7828c2ecf20Sopenharmony_ci ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, 7838c2ecf20Sopenharmony_ci SOF_HDA_DPIB_ENTRY_SIZE * num_total, 7848c2ecf20Sopenharmony_ci &bus->posbuf); 7858c2ecf20Sopenharmony_ci if (ret < 0) { 7868c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: posbuffer dma alloc failed\n"); 7878c2ecf20Sopenharmony_ci return -ENOMEM; 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) 7918c2ecf20Sopenharmony_ci /* mem alloc for the CORB/RIRB ringbuffers */ 7928c2ecf20Sopenharmony_ci ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, 7938c2ecf20Sopenharmony_ci PAGE_SIZE, &bus->rb); 7948c2ecf20Sopenharmony_ci if (ret < 0) { 7958c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: RB alloc failed\n"); 7968c2ecf20Sopenharmony_ci return -ENOMEM; 7978c2ecf20Sopenharmony_ci } 7988c2ecf20Sopenharmony_ci#endif 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci /* create capture streams */ 8018c2ecf20Sopenharmony_ci for (i = 0; i < num_capture; i++) { 8028c2ecf20Sopenharmony_ci struct sof_intel_hda_stream *hda_stream; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci hda_stream = devm_kzalloc(sdev->dev, sizeof(*hda_stream), 8058c2ecf20Sopenharmony_ci GFP_KERNEL); 8068c2ecf20Sopenharmony_ci if (!hda_stream) 8078c2ecf20Sopenharmony_ci return -ENOMEM; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci hda_stream->sdev = sdev; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci stream = &hda_stream->hda_stream; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] + 8148c2ecf20Sopenharmony_ci SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] + 8178c2ecf20Sopenharmony_ci SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total + 8188c2ecf20Sopenharmony_ci SOF_HDA_PPLC_INTERVAL * i; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci /* do we support SPIB */ 8218c2ecf20Sopenharmony_ci if (sdev->bar[HDA_DSP_SPIB_BAR]) { 8228c2ecf20Sopenharmony_ci stream->spib_addr = sdev->bar[HDA_DSP_SPIB_BAR] + 8238c2ecf20Sopenharmony_ci SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i + 8248c2ecf20Sopenharmony_ci SOF_HDA_SPIB_SPIB; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci stream->fifo_addr = sdev->bar[HDA_DSP_SPIB_BAR] + 8278c2ecf20Sopenharmony_ci SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i + 8288c2ecf20Sopenharmony_ci SOF_HDA_SPIB_MAXFIFO; 8298c2ecf20Sopenharmony_ci } 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci hstream = &stream->hstream; 8328c2ecf20Sopenharmony_ci hstream->bus = bus; 8338c2ecf20Sopenharmony_ci hstream->sd_int_sta_mask = 1 << i; 8348c2ecf20Sopenharmony_ci hstream->index = i; 8358c2ecf20Sopenharmony_ci sd_offset = SOF_STREAM_SD_OFFSET(hstream); 8368c2ecf20Sopenharmony_ci hstream->sd_addr = sdev->bar[HDA_DSP_HDA_BAR] + sd_offset; 8378c2ecf20Sopenharmony_ci hstream->stream_tag = i + 1; 8388c2ecf20Sopenharmony_ci hstream->opened = false; 8398c2ecf20Sopenharmony_ci hstream->running = false; 8408c2ecf20Sopenharmony_ci hstream->direction = SNDRV_PCM_STREAM_CAPTURE; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci /* memory alloc for stream BDL */ 8438c2ecf20Sopenharmony_ci ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, 8448c2ecf20Sopenharmony_ci HDA_DSP_BDL_SIZE, &hstream->bdl); 8458c2ecf20Sopenharmony_ci if (ret < 0) { 8468c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: stream bdl dma alloc failed\n"); 8478c2ecf20Sopenharmony_ci return -ENOMEM; 8488c2ecf20Sopenharmony_ci } 8498c2ecf20Sopenharmony_ci hstream->posbuf = (__le32 *)(bus->posbuf.area + 8508c2ecf20Sopenharmony_ci (hstream->index) * 8); 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci list_add_tail(&hstream->list, &bus->stream_list); 8538c2ecf20Sopenharmony_ci } 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci /* create playback streams */ 8568c2ecf20Sopenharmony_ci for (i = num_capture; i < num_total; i++) { 8578c2ecf20Sopenharmony_ci struct sof_intel_hda_stream *hda_stream; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci hda_stream = devm_kzalloc(sdev->dev, sizeof(*hda_stream), 8608c2ecf20Sopenharmony_ci GFP_KERNEL); 8618c2ecf20Sopenharmony_ci if (!hda_stream) 8628c2ecf20Sopenharmony_ci return -ENOMEM; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci hda_stream->sdev = sdev; 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci stream = &hda_stream->hda_stream; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci /* we always have DSP support */ 8698c2ecf20Sopenharmony_ci stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] + 8708c2ecf20Sopenharmony_ci SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] + 8738c2ecf20Sopenharmony_ci SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total + 8748c2ecf20Sopenharmony_ci SOF_HDA_PPLC_INTERVAL * i; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci /* do we support SPIB */ 8778c2ecf20Sopenharmony_ci if (sdev->bar[HDA_DSP_SPIB_BAR]) { 8788c2ecf20Sopenharmony_ci stream->spib_addr = sdev->bar[HDA_DSP_SPIB_BAR] + 8798c2ecf20Sopenharmony_ci SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i + 8808c2ecf20Sopenharmony_ci SOF_HDA_SPIB_SPIB; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci stream->fifo_addr = sdev->bar[HDA_DSP_SPIB_BAR] + 8838c2ecf20Sopenharmony_ci SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i + 8848c2ecf20Sopenharmony_ci SOF_HDA_SPIB_MAXFIFO; 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci hstream = &stream->hstream; 8888c2ecf20Sopenharmony_ci hstream->bus = bus; 8898c2ecf20Sopenharmony_ci hstream->sd_int_sta_mask = 1 << i; 8908c2ecf20Sopenharmony_ci hstream->index = i; 8918c2ecf20Sopenharmony_ci sd_offset = SOF_STREAM_SD_OFFSET(hstream); 8928c2ecf20Sopenharmony_ci hstream->sd_addr = sdev->bar[HDA_DSP_HDA_BAR] + sd_offset; 8938c2ecf20Sopenharmony_ci hstream->stream_tag = i - num_capture + 1; 8948c2ecf20Sopenharmony_ci hstream->opened = false; 8958c2ecf20Sopenharmony_ci hstream->running = false; 8968c2ecf20Sopenharmony_ci hstream->direction = SNDRV_PCM_STREAM_PLAYBACK; 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci /* mem alloc for stream BDL */ 8998c2ecf20Sopenharmony_ci ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, 9008c2ecf20Sopenharmony_ci HDA_DSP_BDL_SIZE, &hstream->bdl); 9018c2ecf20Sopenharmony_ci if (ret < 0) { 9028c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: stream bdl dma alloc failed\n"); 9038c2ecf20Sopenharmony_ci return -ENOMEM; 9048c2ecf20Sopenharmony_ci } 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci hstream->posbuf = (__le32 *)(bus->posbuf.area + 9078c2ecf20Sopenharmony_ci (hstream->index) * 8); 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci list_add_tail(&hstream->list, &bus->stream_list); 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci /* store total stream count (playback + capture) from GCAP */ 9138c2ecf20Sopenharmony_ci sof_hda->stream_max = num_total; 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci return 0; 9168c2ecf20Sopenharmony_ci} 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_civoid hda_dsp_stream_free(struct snd_sof_dev *sdev) 9198c2ecf20Sopenharmony_ci{ 9208c2ecf20Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 9218c2ecf20Sopenharmony_ci struct hdac_stream *s, *_s; 9228c2ecf20Sopenharmony_ci struct hdac_ext_stream *stream; 9238c2ecf20Sopenharmony_ci struct sof_intel_hda_stream *hda_stream; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci /* free position buffer */ 9268c2ecf20Sopenharmony_ci if (bus->posbuf.area) 9278c2ecf20Sopenharmony_ci snd_dma_free_pages(&bus->posbuf); 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) 9308c2ecf20Sopenharmony_ci /* free position buffer */ 9318c2ecf20Sopenharmony_ci if (bus->rb.area) 9328c2ecf20Sopenharmony_ci snd_dma_free_pages(&bus->rb); 9338c2ecf20Sopenharmony_ci#endif 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci list_for_each_entry_safe(s, _s, &bus->stream_list, list) { 9368c2ecf20Sopenharmony_ci /* TODO: decouple */ 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci /* free bdl buffer */ 9398c2ecf20Sopenharmony_ci if (s->bdl.area) 9408c2ecf20Sopenharmony_ci snd_dma_free_pages(&s->bdl); 9418c2ecf20Sopenharmony_ci list_del(&s->list); 9428c2ecf20Sopenharmony_ci stream = stream_to_hdac_ext_stream(s); 9438c2ecf20Sopenharmony_ci hda_stream = container_of(stream, struct sof_intel_hda_stream, 9448c2ecf20Sopenharmony_ci hda_stream); 9458c2ecf20Sopenharmony_ci devm_kfree(sdev->dev, hda_stream); 9468c2ecf20Sopenharmony_ci } 9478c2ecf20Sopenharmony_ci} 948