From patchwork Fri Mar 11 15:35:42 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Cezary Rojewski X-Patchwork-Id: 550510 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id F044FC433EF for ; Fri, 11 Mar 2022 15:33:20 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 3878E1A4E; Fri, 11 Mar 2022 16:32:29 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 3878E1A4E DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1647012799; bh=vgm6mNpjCNZeWX7yolGPJZsPHnxnOVP3l+EINNrg1F8=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=ql/+sb8C021xrNYAF4XuCWCqll4dstEhvwS9pGY3EsgtIJPxLodIVSQV/z+nT5mpc j8eA4UoHmzYTWInKV5sNBjLc/u8K/Cf84gq8TsJVYhkmRRPGG8o37mZfkVpkTAoQ2C gRg9gShIykzTsw6frYIXw8MCeSnkWxn9vhizAjg4= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 190D3F8059F; Fri, 11 Mar 2022 16:28:37 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 7F900F805B4; Fri, 11 Mar 2022 16:28:36 +0100 (CET) Received: from mga07.intel.com (mga07.intel.com [134.134.136.100]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id C9A7BF8059F for ; Fri, 11 Mar 2022 16:28:29 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz C9A7BF8059F Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="O0dXgH41" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1647012511; x=1678548511; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=vgm6mNpjCNZeWX7yolGPJZsPHnxnOVP3l+EINNrg1F8=; b=O0dXgH41g9mUPlVk9sEjWQHa7eYYZ65f5i64AxfV3Mo/OkxTa/cGK1xk lbTfeaoRPpmE52p43E8cx5bC/+t8MCR7HAuw2p1SqpvkLKKb6l8EvpUX8 +g3RDcul/2pxJg3H/bwLnpIsLQnYUDkpOQE0+Lrs/fWJm9v+IiHLjs9nw RgQeQ4zY2X58rUZh4t3el7/fbWqAdFsq7Hv3gvQteNhfV79C/6gLT24TN 8FhyPVuyQaDFznumnRvvZG4U98f4Holyhasobmrkyd8qbtNIr8IKzOh49 oW2afPLb23jEIBQYsgxGrU4PkGNXGftJjX1w1XkDx2v5tYA7ibn8uGFHp g==; X-IronPort-AV: E=McAfee;i="6200,9189,10282"; a="318813431" X-IronPort-AV: E=Sophos;i="5.90,174,1643702400"; d="scan'208";a="318813431" Received: from orsmga006.jf.intel.com ([10.7.209.51]) by orsmga105.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 11 Mar 2022 07:28:29 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.90,174,1643702400"; d="scan'208";a="514532942" Received: from crojewsk-ctrl.igk.intel.com ([10.102.9.28]) by orsmga006.jf.intel.com with ESMTP; 11 Mar 2022 07:28:25 -0800 From: Cezary Rojewski To: alsa-devel@alsa-project.org Subject: [PATCH v5 15/17] ASoC: Intel: avs: Implement CLDMA transfer Date: Fri, 11 Mar 2022 16:35:42 +0100 Message-Id: <20220311153544.136854-16-cezary.rojewski@intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220311153544.136854-1-cezary.rojewski@intel.com> References: <20220311153544.136854-1-cezary.rojewski@intel.com> MIME-Version: 1.0 Cc: Cezary Rojewski , rad@semihalf.com, upstream@semihalf.com, harshapriya.n@intel.com, tiwai@suse.com, pierre-louis.bossart@linux.intel.com, hdegoede@redhat.com, broonie@kernel.org, ranjani.sridharan@linux.intel.com, amadeuszx.slawinski@linux.intel.com, cujomalainey@chromium.org, lma@semihalf.com X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: "Alsa-devel" SKL and KBL rely on a dedicated HDAudio DMA stream for code loading and authentication. The implementation of this specific mechanism for SKL-based platforms re-uses HDAudio DMA (streaming) functions found in HDA library to avoid duplication of functionality. Signed-off-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski --- sound/soc/intel/avs/Makefile | 1 + sound/soc/intel/avs/cldma.c | 316 ++++++++++++++++++++++++++++++++ sound/soc/intel/avs/cldma.h | 29 +++ sound/soc/intel/avs/registers.h | 2 + 4 files changed, 348 insertions(+) create mode 100644 sound/soc/intel/avs/cldma.c create mode 100644 sound/soc/intel/avs/cldma.h diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile index d9c793160612..f842bfc5e97e 100644 --- a/sound/soc/intel/avs/Makefile +++ b/sound/soc/intel/avs/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only snd-soc-avs-objs := dsp.o ipc.o messages.o utils.o core.o loader.o +snd-soc-avs-objs += cldma.o obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o diff --git a/sound/soc/intel/avs/cldma.c b/sound/soc/intel/avs/cldma.c new file mode 100644 index 000000000000..d100c6ba4d8a --- /dev/null +++ b/sound/soc/intel/avs/cldma.c @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// +// Author: Cezary Rojewski +// + +#include +#include +#include +#include "cldma.h" +#include "registers.h" + +/* Stream Registers */ +#define AZX_CL_SD_BASE 0x80 +#define AZX_SD_CTL_STRM_MASK GENMASK(23, 20) +#define AZX_SD_CTL_STRM(s) (((s)->stream_tag << 20) & AZX_SD_CTL_STRM_MASK) +#define AZX_SD_BDLPL_BDLPLBA_MASK GENMASK(31, 7) +#define AZX_SD_BDLPL_BDLPLBA(lb) ((lb) & AZX_SD_BDLPL_BDLPLBA_MASK) + +/* Software Position Based FIFO Capability Registers */ +#define AZX_CL_SPBFCS 0x20 +#define AZX_REG_CL_SPBFCTL (AZX_CL_SPBFCS + 0x4) +#define AZX_REG_CL_SD_SPIB (AZX_CL_SPBFCS + 0x8) + +#define AVS_CL_OP_INTERVAL_US 3 +#define AVS_CL_OP_TIMEOUT_US 300 +#define AVS_CL_IOC_TIMEOUT_MS 300 +#define AVS_CL_STREAM_INDEX 0 + +struct hda_cldma { + struct device *dev; + struct hdac_bus *bus; + void __iomem *dsp_ba; + + unsigned int buffer_size; + unsigned int num_periods; + unsigned int stream_tag; + void __iomem *sd_addr; + + struct snd_dma_buffer dmab_data; + struct snd_dma_buffer dmab_bdl; + struct delayed_work memcpy_work; + struct completion completion; + + /* runtime */ + void *position; + unsigned int remaining; + unsigned int sd_status; +}; + +static void cldma_memcpy_work(struct work_struct *work); + +struct hda_cldma code_loader = { + .stream_tag = AVS_CL_STREAM_INDEX + 1, + .memcpy_work = __DELAYED_WORK_INITIALIZER(code_loader.memcpy_work, cldma_memcpy_work, 0), + .completion = COMPLETION_INITIALIZER(code_loader.completion), +}; + +void hda_cldma_fill(struct hda_cldma *cl) +{ + unsigned int size, offset; + + if (cl->remaining > cl->buffer_size) + size = cl->buffer_size; + else + size = cl->remaining; + + offset = snd_hdac_stream_readl(cl, CL_SD_SPIB); + if (offset + size > cl->buffer_size) { + unsigned int ss; + + ss = cl->buffer_size - offset; + memcpy(cl->dmab_data.area + offset, cl->position, ss); + offset = 0; + size -= ss; + cl->position += ss; + cl->remaining -= ss; + } + + memcpy(cl->dmab_data.area + offset, cl->position, size); + cl->position += size; + cl->remaining -= size; + + snd_hdac_stream_writel(cl, CL_SD_SPIB, offset + size); +} + +static void cldma_memcpy_work(struct work_struct *work) +{ + struct hda_cldma *cl = container_of(work, struct hda_cldma, memcpy_work.work); + int ret; + + ret = hda_cldma_start(cl); + if (ret < 0) { + dev_err(cl->dev, "cldma set RUN failed: %d\n", ret); + return; + } + + while (true) { + ret = wait_for_completion_timeout(&cl->completion, + msecs_to_jiffies(AVS_CL_IOC_TIMEOUT_MS)); + if (!ret) { + dev_err(cl->dev, "cldma IOC timeout\n"); + break; + } + + if (!(cl->sd_status & SD_INT_COMPLETE)) { + dev_err(cl->dev, "cldma transfer error, SD status: 0x%08x\n", + cl->sd_status); + break; + } + + if (!cl->remaining) + break; + + reinit_completion(&cl->completion); + hda_cldma_fill(cl); + /* enable CLDMA interrupt */ + snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_CLDMA, + AVS_ADSP_ADSPIC_CLDMA); + } +} + +void hda_cldma_transfer(struct hda_cldma *cl, unsigned long start_delay) +{ + if (!cl->remaining) + return; + + reinit_completion(&cl->completion); + /* fill buffer with the first chunk before scheduling run */ + hda_cldma_fill(cl); + + schedule_delayed_work(&cl->memcpy_work, start_delay); +} + +int hda_cldma_start(struct hda_cldma *cl) +{ + unsigned int reg; + + /* enable interrupts */ + snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_CLDMA, + AVS_ADSP_ADSPIC_CLDMA); + snd_hdac_stream_updateb(cl, SD_CTL, SD_INT_MASK | SD_CTL_DMA_START, + SD_INT_MASK | SD_CTL_DMA_START); + + /* await DMA engine start */ + return snd_hdac_stream_readb_poll(cl, SD_CTL, reg, reg & SD_CTL_DMA_START, + AVS_CL_OP_INTERVAL_US, AVS_CL_OP_TIMEOUT_US); +} + +int hda_cldma_stop(struct hda_cldma *cl) +{ + unsigned int reg; + int ret; + + /* disable interrupts */ + snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_CLDMA, 0); + snd_hdac_stream_updateb(cl, SD_CTL, SD_INT_MASK | SD_CTL_DMA_START, 0); + + /* await DMA engine stop */ + ret = snd_hdac_stream_readb_poll(cl, SD_CTL, reg, !(reg & SD_CTL_DMA_START), + AVS_CL_OP_INTERVAL_US, AVS_CL_OP_TIMEOUT_US); + cancel_delayed_work_sync(&cl->memcpy_work); + + return ret; +} + +int hda_cldma_reset(struct hda_cldma *cl) +{ + unsigned int reg; + int ret; + + ret = hda_cldma_stop(cl); + if (ret < 0) { + dev_err(cl->dev, "cldma stop failed: %d\n", ret); + return ret; + } + + snd_hdac_stream_updateb(cl, SD_CTL, 1, 1); + ret = snd_hdac_stream_readb_poll(cl, SD_CTL, reg, (reg & 1), AVS_CL_OP_INTERVAL_US, + AVS_CL_OP_TIMEOUT_US); + if (ret < 0) { + dev_err(cl->dev, "cldma set SRST failed: %d\n", ret); + return ret; + } + + snd_hdac_stream_updateb(cl, SD_CTL, 1, 0); + ret = snd_hdac_stream_readb_poll(cl, SD_CTL, reg, !(reg & 1), AVS_CL_OP_INTERVAL_US, + AVS_CL_OP_TIMEOUT_US); + if (ret < 0) { + dev_err(cl->dev, "cldma unset SRST failed: %d\n", ret); + return ret; + } + + return 0; +} + +void hda_cldma_set_data(struct hda_cldma *cl, void *data, unsigned int size) +{ + /* setup runtime */ + cl->position = data; + cl->remaining = size; +} + +static void cldma_setup_bdle(struct hda_cldma *cl, u32 bdle_size) +{ + struct snd_dma_buffer *dmab = &cl->dmab_data; + __le32 *bdl = (__le32 *)cl->dmab_bdl.area; + int remaining = cl->buffer_size; + int offset = 0; + + cl->num_periods = 0; + + while (remaining > 0) { + phys_addr_t addr; + int chunk; + + addr = snd_sgbuf_get_addr(dmab, offset); + bdl[0] = cpu_to_le32(lower_32_bits(addr)); + bdl[1] = cpu_to_le32(upper_32_bits(addr)); + chunk = snd_sgbuf_get_chunk_size(dmab, offset, bdle_size); + bdl[2] = cpu_to_le32(chunk); + + remaining -= chunk; + /* set IOC only for the last entry */ + bdl[3] = (remaining > 0) ? 0 : cpu_to_le32(0x01); + + bdl += 4; + offset += chunk; + cl->num_periods++; + } +} + +void hda_cldma_setup(struct hda_cldma *cl) +{ + dma_addr_t bdl_addr = cl->dmab_bdl.addr; + + cldma_setup_bdle(cl, cl->buffer_size / 2); + + snd_hdac_stream_writel(cl, SD_BDLPL, AZX_SD_BDLPL_BDLPLBA(lower_32_bits(bdl_addr))); + snd_hdac_stream_writel(cl, SD_BDLPU, upper_32_bits(bdl_addr)); + + snd_hdac_stream_writel(cl, SD_CBL, cl->buffer_size); + snd_hdac_stream_writeb(cl, SD_LVI, cl->num_periods - 1); + + snd_hdac_stream_updatel(cl, SD_CTL, AZX_SD_CTL_STRM_MASK, AZX_SD_CTL_STRM(cl)); + /* enable spib */ + snd_hdac_stream_writel(cl, CL_SPBFCTL, 1); +} + +static irqreturn_t cldma_irq_handler(int irq, void *dev_id) +{ + struct hda_cldma *cl = dev_id; + u32 adspis; + + adspis = snd_hdac_adsp_readl(cl, AVS_ADSP_REG_ADSPIS); + if (adspis == UINT_MAX) + return IRQ_NONE; + if (!(adspis & AVS_ADSP_ADSPIS_CLDMA)) + return IRQ_NONE; + + cl->sd_status = snd_hdac_stream_readb(cl, SD_STS); + dev_warn(cl->dev, "%s sd_status: 0x%08x\n", __func__, cl->sd_status); + + /* disable CLDMA interrupt */ + snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_CLDMA, 0); + + complete(&cl->completion); + + return IRQ_HANDLED; +} + +int hda_cldma_init(struct hda_cldma *cl, struct hdac_bus *bus, void __iomem *dsp_ba, + unsigned int buffer_size) +{ + struct pci_dev *pci = to_pci_dev(bus->dev); + int ret; + + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, bus->dev, buffer_size, &cl->dmab_data); + if (ret < 0) + return ret; + + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, bus->dev, BDL_SIZE, &cl->dmab_bdl); + if (ret < 0) + goto alloc_err; + + cl->dev = bus->dev; + cl->bus = bus; + cl->dsp_ba = dsp_ba; + cl->buffer_size = buffer_size; + cl->sd_addr = dsp_ba + AZX_CL_SD_BASE; + + ret = pci_request_irq(pci, 0, cldma_irq_handler, NULL, cl, "CLDMA"); + if (ret < 0) { + dev_err(cl->dev, "Failed to request CLDMA IRQ handler: %d\n", ret); + goto req_err; + } + + return 0; + +req_err: + snd_dma_free_pages(&cl->dmab_bdl); +alloc_err: + snd_dma_free_pages(&cl->dmab_data); + + return ret; +} + +void hda_cldma_free(struct hda_cldma *cl) +{ + struct pci_dev *pci = to_pci_dev(cl->dev); + + pci_free_irq(pci, 0, cl); + snd_dma_free_pages(&cl->dmab_data); + snd_dma_free_pages(&cl->dmab_bdl); +} diff --git a/sound/soc/intel/avs/cldma.h b/sound/soc/intel/avs/cldma.h new file mode 100644 index 000000000000..754fcf9ee585 --- /dev/null +++ b/sound/soc/intel/avs/cldma.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright(c) 2021-2022 Intel Corporation. All rights reserved. + * + * Author: Cezary Rojewski + */ + +#ifndef __SOUND_SOC_INTEL_AVS_CLDMA_H +#define __SOUND_SOC_INTEL_AVS_CLDMA_H + +#define AVS_CL_DEFAULT_BUFFER_SIZE (32 * PAGE_SIZE) + +struct hda_cldma; +extern struct hda_cldma code_loader; + +void hda_cldma_fill(struct hda_cldma *cl); +void hda_cldma_transfer(struct hda_cldma *cl, unsigned long start_delay); + +int hda_cldma_start(struct hda_cldma *cl); +int hda_cldma_stop(struct hda_cldma *cl); +int hda_cldma_reset(struct hda_cldma *cl); + +void hda_cldma_set_data(struct hda_cldma *cl, void *data, unsigned int size); +void hda_cldma_setup(struct hda_cldma *cl); +int hda_cldma_init(struct hda_cldma *cl, struct hdac_bus *bus, void __iomem *dsp_ba, + unsigned int buffer_size); +void hda_cldma_free(struct hda_cldma *cl); + +#endif diff --git a/sound/soc/intel/avs/registers.h b/sound/soc/intel/avs/registers.h index 73699d46821b..3fd02389ed2b 100644 --- a/sound/soc/intel/avs/registers.h +++ b/sound/soc/intel/avs/registers.h @@ -22,7 +22,9 @@ #define AVS_ADSP_REG_ADSPIS (AVS_ADSP_GEN_BASE + 0x0C) #define AVS_ADSP_ADSPIC_IPC BIT(0) +#define AVS_ADSP_ADSPIC_CLDMA BIT(1) #define AVS_ADSP_ADSPIS_IPC BIT(0) +#define AVS_ADSP_ADSPIS_CLDMA BIT(1) #define AVS_ADSPCS_CRST_MASK(cm) (cm) #define AVS_ADSPCS_CSTALL_MASK(cm) ((cm) << 8)