162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Author: Cezary Rojewski <cezary.rojewski@intel.com> 662306a36Sopenharmony_ci// 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/pci.h> 962306a36Sopenharmony_ci#include <sound/hda_register.h> 1062306a36Sopenharmony_ci#include <sound/hdaudio_ext.h> 1162306a36Sopenharmony_ci#include "cldma.h" 1262306a36Sopenharmony_ci#include "registers.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/* Stream Registers */ 1562306a36Sopenharmony_ci#define AZX_CL_SD_BASE 0x80 1662306a36Sopenharmony_ci#define AZX_SD_CTL_STRM_MASK GENMASK(23, 20) 1762306a36Sopenharmony_ci#define AZX_SD_CTL_STRM(s) (((s)->stream_tag << 20) & AZX_SD_CTL_STRM_MASK) 1862306a36Sopenharmony_ci#define AZX_SD_BDLPL_BDLPLBA_MASK GENMASK(31, 7) 1962306a36Sopenharmony_ci#define AZX_SD_BDLPL_BDLPLBA(lb) ((lb) & AZX_SD_BDLPL_BDLPLBA_MASK) 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* Software Position Based FIFO Capability Registers */ 2262306a36Sopenharmony_ci#define AZX_CL_SPBFCS 0x20 2362306a36Sopenharmony_ci#define AZX_REG_CL_SPBFCTL (AZX_CL_SPBFCS + 0x4) 2462306a36Sopenharmony_ci#define AZX_REG_CL_SD_SPIB (AZX_CL_SPBFCS + 0x8) 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define AVS_CL_OP_INTERVAL_US 3 2762306a36Sopenharmony_ci#define AVS_CL_OP_TIMEOUT_US 300 2862306a36Sopenharmony_ci#define AVS_CL_IOC_TIMEOUT_MS 300 2962306a36Sopenharmony_ci#define AVS_CL_STREAM_INDEX 0 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistruct hda_cldma { 3262306a36Sopenharmony_ci struct device *dev; 3362306a36Sopenharmony_ci struct hdac_bus *bus; 3462306a36Sopenharmony_ci void __iomem *dsp_ba; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci unsigned int buffer_size; 3762306a36Sopenharmony_ci unsigned int num_periods; 3862306a36Sopenharmony_ci unsigned int stream_tag; 3962306a36Sopenharmony_ci void __iomem *sd_addr; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci struct snd_dma_buffer dmab_data; 4262306a36Sopenharmony_ci struct snd_dma_buffer dmab_bdl; 4362306a36Sopenharmony_ci struct delayed_work memcpy_work; 4462306a36Sopenharmony_ci struct completion completion; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci /* runtime */ 4762306a36Sopenharmony_ci void *position; 4862306a36Sopenharmony_ci unsigned int remaining; 4962306a36Sopenharmony_ci unsigned int sd_status; 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic void cldma_memcpy_work(struct work_struct *work); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistruct hda_cldma code_loader = { 5562306a36Sopenharmony_ci .stream_tag = AVS_CL_STREAM_INDEX + 1, 5662306a36Sopenharmony_ci .memcpy_work = __DELAYED_WORK_INITIALIZER(code_loader.memcpy_work, cldma_memcpy_work, 0), 5762306a36Sopenharmony_ci .completion = COMPLETION_INITIALIZER(code_loader.completion), 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_civoid hda_cldma_fill(struct hda_cldma *cl) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci unsigned int size, offset; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (cl->remaining > cl->buffer_size) 6562306a36Sopenharmony_ci size = cl->buffer_size; 6662306a36Sopenharmony_ci else 6762306a36Sopenharmony_ci size = cl->remaining; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci offset = snd_hdac_stream_readl(cl, CL_SD_SPIB); 7062306a36Sopenharmony_ci if (offset + size > cl->buffer_size) { 7162306a36Sopenharmony_ci unsigned int ss; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci ss = cl->buffer_size - offset; 7462306a36Sopenharmony_ci memcpy(cl->dmab_data.area + offset, cl->position, ss); 7562306a36Sopenharmony_ci offset = 0; 7662306a36Sopenharmony_ci size -= ss; 7762306a36Sopenharmony_ci cl->position += ss; 7862306a36Sopenharmony_ci cl->remaining -= ss; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci memcpy(cl->dmab_data.area + offset, cl->position, size); 8262306a36Sopenharmony_ci cl->position += size; 8362306a36Sopenharmony_ci cl->remaining -= size; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci snd_hdac_stream_writel(cl, CL_SD_SPIB, offset + size); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic void cldma_memcpy_work(struct work_struct *work) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci struct hda_cldma *cl = container_of(work, struct hda_cldma, memcpy_work.work); 9162306a36Sopenharmony_ci int ret; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci ret = hda_cldma_start(cl); 9462306a36Sopenharmony_ci if (ret < 0) { 9562306a36Sopenharmony_ci dev_err(cl->dev, "cldma set RUN failed: %d\n", ret); 9662306a36Sopenharmony_ci return; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci while (true) { 10062306a36Sopenharmony_ci ret = wait_for_completion_timeout(&cl->completion, 10162306a36Sopenharmony_ci msecs_to_jiffies(AVS_CL_IOC_TIMEOUT_MS)); 10262306a36Sopenharmony_ci if (!ret) { 10362306a36Sopenharmony_ci dev_err(cl->dev, "cldma IOC timeout\n"); 10462306a36Sopenharmony_ci break; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (!(cl->sd_status & SD_INT_COMPLETE)) { 10862306a36Sopenharmony_ci dev_err(cl->dev, "cldma transfer error, SD status: 0x%08x\n", 10962306a36Sopenharmony_ci cl->sd_status); 11062306a36Sopenharmony_ci break; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (!cl->remaining) 11462306a36Sopenharmony_ci break; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci reinit_completion(&cl->completion); 11762306a36Sopenharmony_ci hda_cldma_fill(cl); 11862306a36Sopenharmony_ci /* enable CLDMA interrupt */ 11962306a36Sopenharmony_ci snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_CLDMA, 12062306a36Sopenharmony_ci AVS_ADSP_ADSPIC_CLDMA); 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_civoid hda_cldma_transfer(struct hda_cldma *cl, unsigned long start_delay) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci if (!cl->remaining) 12762306a36Sopenharmony_ci return; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci reinit_completion(&cl->completion); 13062306a36Sopenharmony_ci /* fill buffer with the first chunk before scheduling run */ 13162306a36Sopenharmony_ci hda_cldma_fill(cl); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci schedule_delayed_work(&cl->memcpy_work, start_delay); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ciint hda_cldma_start(struct hda_cldma *cl) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci unsigned int reg; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci /* enable interrupts */ 14162306a36Sopenharmony_ci snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_CLDMA, 14262306a36Sopenharmony_ci AVS_ADSP_ADSPIC_CLDMA); 14362306a36Sopenharmony_ci snd_hdac_stream_updateb(cl, SD_CTL, SD_INT_MASK | SD_CTL_DMA_START, 14462306a36Sopenharmony_ci SD_INT_MASK | SD_CTL_DMA_START); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* await DMA engine start */ 14762306a36Sopenharmony_ci return snd_hdac_stream_readb_poll(cl, SD_CTL, reg, reg & SD_CTL_DMA_START, 14862306a36Sopenharmony_ci AVS_CL_OP_INTERVAL_US, AVS_CL_OP_TIMEOUT_US); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ciint hda_cldma_stop(struct hda_cldma *cl) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci unsigned int reg; 15462306a36Sopenharmony_ci int ret; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* disable interrupts */ 15762306a36Sopenharmony_ci snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_CLDMA, 0); 15862306a36Sopenharmony_ci snd_hdac_stream_updateb(cl, SD_CTL, SD_INT_MASK | SD_CTL_DMA_START, 0); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* await DMA engine stop */ 16162306a36Sopenharmony_ci ret = snd_hdac_stream_readb_poll(cl, SD_CTL, reg, !(reg & SD_CTL_DMA_START), 16262306a36Sopenharmony_ci AVS_CL_OP_INTERVAL_US, AVS_CL_OP_TIMEOUT_US); 16362306a36Sopenharmony_ci cancel_delayed_work_sync(&cl->memcpy_work); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci return ret; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ciint hda_cldma_reset(struct hda_cldma *cl) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci unsigned int reg; 17162306a36Sopenharmony_ci int ret; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci ret = hda_cldma_stop(cl); 17462306a36Sopenharmony_ci if (ret < 0) { 17562306a36Sopenharmony_ci dev_err(cl->dev, "cldma stop failed: %d\n", ret); 17662306a36Sopenharmony_ci return ret; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci snd_hdac_stream_updateb(cl, SD_CTL, SD_CTL_STREAM_RESET, SD_CTL_STREAM_RESET); 18062306a36Sopenharmony_ci ret = snd_hdac_stream_readb_poll(cl, SD_CTL, reg, (reg & SD_CTL_STREAM_RESET), 18162306a36Sopenharmony_ci AVS_CL_OP_INTERVAL_US, AVS_CL_OP_TIMEOUT_US); 18262306a36Sopenharmony_ci if (ret < 0) { 18362306a36Sopenharmony_ci dev_err(cl->dev, "cldma set SRST failed: %d\n", ret); 18462306a36Sopenharmony_ci return ret; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci snd_hdac_stream_updateb(cl, SD_CTL, SD_CTL_STREAM_RESET, 0); 18862306a36Sopenharmony_ci ret = snd_hdac_stream_readb_poll(cl, SD_CTL, reg, !(reg & SD_CTL_STREAM_RESET), 18962306a36Sopenharmony_ci AVS_CL_OP_INTERVAL_US, AVS_CL_OP_TIMEOUT_US); 19062306a36Sopenharmony_ci if (ret < 0) { 19162306a36Sopenharmony_ci dev_err(cl->dev, "cldma unset SRST failed: %d\n", ret); 19262306a36Sopenharmony_ci return ret; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci return 0; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_civoid hda_cldma_set_data(struct hda_cldma *cl, void *data, unsigned int size) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci /* setup runtime */ 20162306a36Sopenharmony_ci cl->position = data; 20262306a36Sopenharmony_ci cl->remaining = size; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic void cldma_setup_bdle(struct hda_cldma *cl, u32 bdle_size) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct snd_dma_buffer *dmab = &cl->dmab_data; 20862306a36Sopenharmony_ci __le32 *bdl = (__le32 *)cl->dmab_bdl.area; 20962306a36Sopenharmony_ci int remaining = cl->buffer_size; 21062306a36Sopenharmony_ci int offset = 0; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci cl->num_periods = 0; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci while (remaining > 0) { 21562306a36Sopenharmony_ci phys_addr_t addr; 21662306a36Sopenharmony_ci int chunk; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci addr = snd_sgbuf_get_addr(dmab, offset); 21962306a36Sopenharmony_ci bdl[0] = cpu_to_le32(lower_32_bits(addr)); 22062306a36Sopenharmony_ci bdl[1] = cpu_to_le32(upper_32_bits(addr)); 22162306a36Sopenharmony_ci chunk = snd_sgbuf_get_chunk_size(dmab, offset, bdle_size); 22262306a36Sopenharmony_ci bdl[2] = cpu_to_le32(chunk); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci remaining -= chunk; 22562306a36Sopenharmony_ci /* set IOC only for the last entry */ 22662306a36Sopenharmony_ci bdl[3] = (remaining > 0) ? 0 : cpu_to_le32(0x01); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci bdl += 4; 22962306a36Sopenharmony_ci offset += chunk; 23062306a36Sopenharmony_ci cl->num_periods++; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_civoid hda_cldma_setup(struct hda_cldma *cl) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci dma_addr_t bdl_addr = cl->dmab_bdl.addr; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci cldma_setup_bdle(cl, cl->buffer_size / 2); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci snd_hdac_stream_writel(cl, SD_BDLPL, AZX_SD_BDLPL_BDLPLBA(lower_32_bits(bdl_addr))); 24162306a36Sopenharmony_ci snd_hdac_stream_writel(cl, SD_BDLPU, upper_32_bits(bdl_addr)); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci snd_hdac_stream_writel(cl, SD_CBL, cl->buffer_size); 24462306a36Sopenharmony_ci snd_hdac_stream_writeb(cl, SD_LVI, cl->num_periods - 1); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci snd_hdac_stream_updatel(cl, SD_CTL, AZX_SD_CTL_STRM_MASK, AZX_SD_CTL_STRM(cl)); 24762306a36Sopenharmony_ci /* enable spib */ 24862306a36Sopenharmony_ci snd_hdac_stream_writel(cl, CL_SPBFCTL, 1); 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic irqreturn_t cldma_irq_handler(int irq, void *dev_id) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci struct hda_cldma *cl = dev_id; 25462306a36Sopenharmony_ci u32 adspis; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci adspis = snd_hdac_adsp_readl(cl, AVS_ADSP_REG_ADSPIS); 25762306a36Sopenharmony_ci if (adspis == UINT_MAX) 25862306a36Sopenharmony_ci return IRQ_NONE; 25962306a36Sopenharmony_ci if (!(adspis & AVS_ADSP_ADSPIS_CLDMA)) 26062306a36Sopenharmony_ci return IRQ_NONE; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci cl->sd_status = snd_hdac_stream_readb(cl, SD_STS); 26362306a36Sopenharmony_ci dev_warn(cl->dev, "%s sd_status: 0x%08x\n", __func__, cl->sd_status); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* disable CLDMA interrupt */ 26662306a36Sopenharmony_ci snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_CLDMA, 0); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci complete(&cl->completion); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci return IRQ_HANDLED; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ciint hda_cldma_init(struct hda_cldma *cl, struct hdac_bus *bus, void __iomem *dsp_ba, 27462306a36Sopenharmony_ci unsigned int buffer_size) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci struct pci_dev *pci = to_pci_dev(bus->dev); 27762306a36Sopenharmony_ci int ret; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, bus->dev, buffer_size, &cl->dmab_data); 28062306a36Sopenharmony_ci if (ret < 0) 28162306a36Sopenharmony_ci return ret; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, bus->dev, BDL_SIZE, &cl->dmab_bdl); 28462306a36Sopenharmony_ci if (ret < 0) 28562306a36Sopenharmony_ci goto alloc_err; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci cl->dev = bus->dev; 28862306a36Sopenharmony_ci cl->bus = bus; 28962306a36Sopenharmony_ci cl->dsp_ba = dsp_ba; 29062306a36Sopenharmony_ci cl->buffer_size = buffer_size; 29162306a36Sopenharmony_ci cl->sd_addr = dsp_ba + AZX_CL_SD_BASE; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci ret = pci_request_irq(pci, 0, cldma_irq_handler, NULL, cl, "CLDMA"); 29462306a36Sopenharmony_ci if (ret < 0) { 29562306a36Sopenharmony_ci dev_err(cl->dev, "Failed to request CLDMA IRQ handler: %d\n", ret); 29662306a36Sopenharmony_ci goto req_err; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci return 0; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cireq_err: 30262306a36Sopenharmony_ci snd_dma_free_pages(&cl->dmab_bdl); 30362306a36Sopenharmony_cialloc_err: 30462306a36Sopenharmony_ci snd_dma_free_pages(&cl->dmab_data); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci return ret; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_civoid hda_cldma_free(struct hda_cldma *cl) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct pci_dev *pci = to_pci_dev(cl->dev); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci pci_free_irq(pci, 0, cl); 31462306a36Sopenharmony_ci snd_dma_free_pages(&cl->dmab_data); 31562306a36Sopenharmony_ci snd_dma_free_pages(&cl->dmab_bdl); 31662306a36Sopenharmony_ci} 317