brintos

brintos / linux-shallow public Read only

0
0
Text · 7.2 KiB · 61326d7 Raw
291 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2//3// Copyright(c) 2021-2022 Intel Corporation4//5// Author: Cezary Rojewski <cezary.rojewski@intel.com>6//7 8#include <linux/pci.h>9#include <sound/hda_register.h>10#include <sound/hdaudio_ext.h>11#include "cldma.h"12#include "registers.h"13 14/* Stream Registers */15#define AZX_CL_SD_BASE			0x8016#define AZX_SD_CTL_STRM_MASK		GENMASK(23, 20)17#define AZX_SD_CTL_STRM(s)		(((s)->stream_tag << 20) & AZX_SD_CTL_STRM_MASK)18#define AZX_SD_BDLPL_BDLPLBA_MASK	GENMASK(31, 7)19#define AZX_SD_BDLPL_BDLPLBA(lb)	((lb) & AZX_SD_BDLPL_BDLPLBA_MASK)20 21/* Software Position Based FIFO Capability Registers */22#define AZX_CL_SPBFCS			0x2023#define AZX_REG_CL_SPBFCTL		(AZX_CL_SPBFCS + 0x4)24#define AZX_REG_CL_SD_SPIB		(AZX_CL_SPBFCS + 0x8)25 26#define AVS_CL_OP_INTERVAL_US		327#define AVS_CL_OP_TIMEOUT_US		30028#define AVS_CL_IOC_TIMEOUT_MS		30029#define AVS_CL_STREAM_INDEX		030 31struct hda_cldma {32	struct device *dev;33	struct hdac_bus *bus;34	void __iomem *dsp_ba;35 36	unsigned int buffer_size;37	unsigned int num_periods;38	unsigned char stream_tag;39	void __iomem *sd_addr;40 41	struct snd_dma_buffer dmab_data;42	struct snd_dma_buffer dmab_bdl;43	struct delayed_work memcpy_work;44	struct completion completion;45 46	/* runtime */47	void *position;48	unsigned int remaining;49	unsigned int sd_status;50};51 52static void cldma_memcpy_work(struct work_struct *work);53 54struct hda_cldma code_loader = {55	.stream_tag	= AVS_CL_STREAM_INDEX + 1,56	.memcpy_work	= __DELAYED_WORK_INITIALIZER(code_loader.memcpy_work, cldma_memcpy_work, 0),57	.completion	= COMPLETION_INITIALIZER(code_loader.completion),58};59 60void hda_cldma_fill(struct hda_cldma *cl)61{62	unsigned int size, offset;63 64	if (cl->remaining > cl->buffer_size)65		size = cl->buffer_size;66	else67		size = cl->remaining;68 69	offset = snd_hdac_stream_readl(cl, CL_SD_SPIB);70	if (offset + size > cl->buffer_size) {71		unsigned int ss;72 73		ss = cl->buffer_size - offset;74		memcpy(cl->dmab_data.area + offset, cl->position, ss);75		offset = 0;76		size -= ss;77		cl->position += ss;78		cl->remaining -= ss;79	}80 81	memcpy(cl->dmab_data.area + offset, cl->position, size);82	cl->position += size;83	cl->remaining -= size;84 85	snd_hdac_stream_writel(cl, CL_SD_SPIB, offset + size);86}87 88static void cldma_memcpy_work(struct work_struct *work)89{90	struct hda_cldma *cl = container_of(work, struct hda_cldma, memcpy_work.work);91	int ret;92 93	ret = hda_cldma_start(cl);94	if (ret < 0) {95		dev_err(cl->dev, "cldma set RUN failed: %d\n", ret);96		return;97	}98 99	while (true) {100		ret = wait_for_completion_timeout(&cl->completion,101						  msecs_to_jiffies(AVS_CL_IOC_TIMEOUT_MS));102		if (!ret) {103			dev_err(cl->dev, "cldma IOC timeout\n");104			break;105		}106 107		if (!(cl->sd_status & SD_INT_COMPLETE)) {108			dev_err(cl->dev, "cldma transfer error, SD status: 0x%08x\n",109				cl->sd_status);110			break;111		}112 113		if (!cl->remaining)114			break;115 116		reinit_completion(&cl->completion);117		hda_cldma_fill(cl);118		/* enable CLDMA interrupt */119		snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_CLDMA,120				      AVS_ADSP_ADSPIC_CLDMA);121	}122}123 124void hda_cldma_transfer(struct hda_cldma *cl, unsigned long start_delay)125{126	if (!cl->remaining)127		return;128 129	reinit_completion(&cl->completion);130	/* fill buffer with the first chunk before scheduling run */131	hda_cldma_fill(cl);132 133	schedule_delayed_work(&cl->memcpy_work, start_delay);134}135 136int hda_cldma_start(struct hda_cldma *cl)137{138	unsigned int reg;139 140	/* enable interrupts */141	snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_CLDMA,142			      AVS_ADSP_ADSPIC_CLDMA);143	snd_hdac_stream_updateb(cl, SD_CTL, SD_INT_MASK | SD_CTL_DMA_START,144				SD_INT_MASK | SD_CTL_DMA_START);145 146	/* await DMA engine start */147	return snd_hdac_stream_readb_poll(cl, SD_CTL, reg, reg & SD_CTL_DMA_START,148					  AVS_CL_OP_INTERVAL_US, AVS_CL_OP_TIMEOUT_US);149}150 151int hda_cldma_stop(struct hda_cldma *cl)152{153	unsigned int reg;154	int ret;155 156	/* disable interrupts */157	snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_CLDMA, 0);158	snd_hdac_stream_updateb(cl, SD_CTL, SD_INT_MASK | SD_CTL_DMA_START, 0);159 160	/* await DMA engine stop */161	ret = snd_hdac_stream_readb_poll(cl, SD_CTL, reg, !(reg & SD_CTL_DMA_START),162					 AVS_CL_OP_INTERVAL_US, AVS_CL_OP_TIMEOUT_US);163	cancel_delayed_work_sync(&cl->memcpy_work);164 165	return ret;166}167 168int hda_cldma_reset(struct hda_cldma *cl)169{170	unsigned int reg;171	int ret;172 173	ret = hda_cldma_stop(cl);174	if (ret < 0) {175		dev_err(cl->dev, "cldma stop failed: %d\n", ret);176		return ret;177	}178 179	snd_hdac_stream_updateb(cl, SD_CTL, SD_CTL_STREAM_RESET, SD_CTL_STREAM_RESET);180	ret = snd_hdac_stream_readb_poll(cl, SD_CTL, reg, (reg & SD_CTL_STREAM_RESET),181					 AVS_CL_OP_INTERVAL_US, AVS_CL_OP_TIMEOUT_US);182	if (ret < 0) {183		dev_err(cl->dev, "cldma set SRST failed: %d\n", ret);184		return ret;185	}186 187	snd_hdac_stream_updateb(cl, SD_CTL, SD_CTL_STREAM_RESET, 0);188	ret = snd_hdac_stream_readb_poll(cl, SD_CTL, reg, !(reg & SD_CTL_STREAM_RESET),189					 AVS_CL_OP_INTERVAL_US, AVS_CL_OP_TIMEOUT_US);190	if (ret < 0) {191		dev_err(cl->dev, "cldma unset SRST failed: %d\n", ret);192		return ret;193	}194 195	return 0;196}197 198void hda_cldma_set_data(struct hda_cldma *cl, void *data, unsigned int size)199{200	/* setup runtime */201	cl->position = data;202	cl->remaining = size;203}204 205static void cldma_setup_bdle(struct hda_cldma *cl, u32 bdle_size)206{207	struct snd_dma_buffer *dmab = &cl->dmab_data;208	__le32 *bdl = (__le32 *)cl->dmab_bdl.area;209	int remaining = cl->buffer_size;210	int offset = 0;211 212	cl->num_periods = 0;213 214	while (remaining > 0) {215		phys_addr_t addr;216		int chunk;217 218		addr = snd_sgbuf_get_addr(dmab, offset);219		bdl[0] = cpu_to_le32(lower_32_bits(addr));220		bdl[1] = cpu_to_le32(upper_32_bits(addr));221		chunk = snd_sgbuf_get_chunk_size(dmab, offset, bdle_size);222		bdl[2] = cpu_to_le32(chunk);223 224		remaining -= chunk;225		/* set IOC only for the last entry */226		bdl[3] = (remaining > 0) ? 0 : cpu_to_le32(0x01);227 228		bdl += 4;229		offset += chunk;230		cl->num_periods++;231	}232}233 234void hda_cldma_setup(struct hda_cldma *cl)235{236	dma_addr_t bdl_addr = cl->dmab_bdl.addr;237 238	cldma_setup_bdle(cl, cl->buffer_size / 2);239 240	snd_hdac_stream_writel(cl, SD_BDLPL, AZX_SD_BDLPL_BDLPLBA(lower_32_bits(bdl_addr)));241	snd_hdac_stream_writel(cl, SD_BDLPU, upper_32_bits(bdl_addr));242 243	snd_hdac_stream_writel(cl, SD_CBL, cl->buffer_size);244	snd_hdac_stream_writeb(cl, SD_LVI, cl->num_periods - 1);245 246	snd_hdac_stream_updatel(cl, SD_CTL, AZX_SD_CTL_STRM_MASK, AZX_SD_CTL_STRM(cl));247	/* enable spib */248	snd_hdac_stream_writel(cl, CL_SPBFCTL, 1);249}250 251void hda_cldma_interrupt(struct hda_cldma *cl)252{253	/* disable CLDMA interrupt */254	snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_CLDMA, 0);255 256	cl->sd_status = snd_hdac_stream_readb(cl, SD_STS);257	dev_dbg(cl->dev, "%s sd_status: 0x%08x\n", __func__, cl->sd_status);258 259	complete(&cl->completion);260}261 262int hda_cldma_init(struct hda_cldma *cl, struct hdac_bus *bus, void __iomem *dsp_ba,263		   unsigned int buffer_size)264{265	int ret;266 267	ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, bus->dev, buffer_size, &cl->dmab_data);268	if (ret < 0)269		return ret;270 271	ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, bus->dev, BDL_SIZE, &cl->dmab_bdl);272	if (ret < 0) {273		snd_dma_free_pages(&cl->dmab_data);274		return ret;275	}276 277	cl->dev = bus->dev;278	cl->bus = bus;279	cl->dsp_ba = dsp_ba;280	cl->buffer_size = buffer_size;281	cl->sd_addr = dsp_ba + AZX_CL_SD_BASE;282 283	return 0;284}285 286void hda_cldma_free(struct hda_cldma *cl)287{288	snd_dma_free_pages(&cl->dmab_data);289	snd_dma_free_pages(&cl->dmab_bdl);290}291