18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci//
38c2ecf20Sopenharmony_ci// Copyright (c) 2020 BayLibre, SAS.
48c2ecf20Sopenharmony_ci// Author: Jerome Brunet <jbrunet@baylibre.com>
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/bitfield.h>
78c2ecf20Sopenharmony_ci#include <linux/clk.h>
88c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
98c2ecf20Sopenharmony_ci#include <sound/pcm_params.h>
108c2ecf20Sopenharmony_ci#include <sound/soc.h>
118c2ecf20Sopenharmony_ci#include <sound/soc-dai.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include "aiu-fifo.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#define AIU_MEM_START	0x00
168c2ecf20Sopenharmony_ci#define AIU_MEM_RD	0x04
178c2ecf20Sopenharmony_ci#define AIU_MEM_END	0x08
188c2ecf20Sopenharmony_ci#define AIU_MEM_MASKS	0x0c
198c2ecf20Sopenharmony_ci#define  AIU_MEM_MASK_CH_RD GENMASK(7, 0)
208c2ecf20Sopenharmony_ci#define  AIU_MEM_MASK_CH_MEM GENMASK(15, 8)
218c2ecf20Sopenharmony_ci#define AIU_MEM_CONTROL	0x10
228c2ecf20Sopenharmony_ci#define  AIU_MEM_CONTROL_INIT BIT(0)
238c2ecf20Sopenharmony_ci#define  AIU_MEM_CONTROL_FILL_EN BIT(1)
248c2ecf20Sopenharmony_ci#define  AIU_MEM_CONTROL_EMPTY_EN BIT(2)
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic struct snd_soc_dai *aiu_fifo_dai(struct snd_pcm_substream *ss)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = ss->private_data;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	return asoc_rtd_to_cpu(rtd, 0);
318c2ecf20Sopenharmony_ci}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cisnd_pcm_uframes_t aiu_fifo_pointer(struct snd_soc_component *component,
348c2ecf20Sopenharmony_ci				   struct snd_pcm_substream *substream)
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	struct snd_soc_dai *dai = aiu_fifo_dai(substream);
378c2ecf20Sopenharmony_ci	struct aiu_fifo *fifo = dai->playback_dma_data;
388c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
398c2ecf20Sopenharmony_ci	unsigned int addr;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	addr = snd_soc_component_read(component, fifo->mem_offset + AIU_MEM_RD);
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	return bytes_to_frames(runtime, addr - (unsigned int)runtime->dma_addr);
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic void aiu_fifo_enable(struct snd_soc_dai *dai, bool enable)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	struct snd_soc_component *component = dai->component;
498c2ecf20Sopenharmony_ci	struct aiu_fifo *fifo = dai->playback_dma_data;
508c2ecf20Sopenharmony_ci	unsigned int en_mask = (AIU_MEM_CONTROL_FILL_EN |
518c2ecf20Sopenharmony_ci				AIU_MEM_CONTROL_EMPTY_EN);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	snd_soc_component_update_bits(component,
548c2ecf20Sopenharmony_ci				      fifo->mem_offset + AIU_MEM_CONTROL,
558c2ecf20Sopenharmony_ci				      en_mask, enable ? en_mask : 0);
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ciint aiu_fifo_trigger(struct snd_pcm_substream *substream, int cmd,
598c2ecf20Sopenharmony_ci		     struct snd_soc_dai *dai)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	switch (cmd) {
628c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
638c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
648c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
658c2ecf20Sopenharmony_ci		aiu_fifo_enable(dai, true);
668c2ecf20Sopenharmony_ci		break;
678c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
688c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
698c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
708c2ecf20Sopenharmony_ci		aiu_fifo_enable(dai, false);
718c2ecf20Sopenharmony_ci		break;
728c2ecf20Sopenharmony_ci	default:
738c2ecf20Sopenharmony_ci		return -EINVAL;
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	return 0;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ciint aiu_fifo_prepare(struct snd_pcm_substream *substream,
808c2ecf20Sopenharmony_ci		     struct snd_soc_dai *dai)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	struct snd_soc_component *component = dai->component;
838c2ecf20Sopenharmony_ci	struct aiu_fifo *fifo = dai->playback_dma_data;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	snd_soc_component_update_bits(component,
868c2ecf20Sopenharmony_ci				      fifo->mem_offset + AIU_MEM_CONTROL,
878c2ecf20Sopenharmony_ci				      AIU_MEM_CONTROL_INIT,
888c2ecf20Sopenharmony_ci				      AIU_MEM_CONTROL_INIT);
898c2ecf20Sopenharmony_ci	snd_soc_component_update_bits(component,
908c2ecf20Sopenharmony_ci				      fifo->mem_offset + AIU_MEM_CONTROL,
918c2ecf20Sopenharmony_ci				      AIU_MEM_CONTROL_INIT, 0);
928c2ecf20Sopenharmony_ci	return 0;
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ciint aiu_fifo_hw_params(struct snd_pcm_substream *substream,
968c2ecf20Sopenharmony_ci		       struct snd_pcm_hw_params *params,
978c2ecf20Sopenharmony_ci		       struct snd_soc_dai *dai)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
1008c2ecf20Sopenharmony_ci	struct snd_soc_component *component = dai->component;
1018c2ecf20Sopenharmony_ci	struct aiu_fifo *fifo = dai->playback_dma_data;
1028c2ecf20Sopenharmony_ci	dma_addr_t end;
1038c2ecf20Sopenharmony_ci	int ret;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
1068c2ecf20Sopenharmony_ci	if (ret < 0)
1078c2ecf20Sopenharmony_ci		return ret;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	/* Setup the fifo boundaries */
1108c2ecf20Sopenharmony_ci	end = runtime->dma_addr + runtime->dma_bytes - fifo->fifo_block;
1118c2ecf20Sopenharmony_ci	snd_soc_component_write(component, fifo->mem_offset + AIU_MEM_START,
1128c2ecf20Sopenharmony_ci				runtime->dma_addr);
1138c2ecf20Sopenharmony_ci	snd_soc_component_write(component, fifo->mem_offset + AIU_MEM_RD,
1148c2ecf20Sopenharmony_ci				runtime->dma_addr);
1158c2ecf20Sopenharmony_ci	snd_soc_component_write(component, fifo->mem_offset + AIU_MEM_END,
1168c2ecf20Sopenharmony_ci				end);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	/* Setup the fifo to read all the memory - no skip */
1198c2ecf20Sopenharmony_ci	snd_soc_component_update_bits(component,
1208c2ecf20Sopenharmony_ci				      fifo->mem_offset + AIU_MEM_MASKS,
1218c2ecf20Sopenharmony_ci				      AIU_MEM_MASK_CH_RD | AIU_MEM_MASK_CH_MEM,
1228c2ecf20Sopenharmony_ci				      FIELD_PREP(AIU_MEM_MASK_CH_RD, 0xff) |
1238c2ecf20Sopenharmony_ci				      FIELD_PREP(AIU_MEM_MASK_CH_MEM, 0xff));
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	return 0;
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ciint aiu_fifo_hw_free(struct snd_pcm_substream *substream,
1298c2ecf20Sopenharmony_ci		     struct snd_soc_dai *dai)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	return snd_pcm_lib_free_pages(substream);
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic irqreturn_t aiu_fifo_isr(int irq, void *dev_id)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	struct snd_pcm_substream *playback = dev_id;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	snd_pcm_period_elapsed(playback);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ciint aiu_fifo_startup(struct snd_pcm_substream *substream,
1448c2ecf20Sopenharmony_ci		     struct snd_soc_dai *dai)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	struct aiu_fifo *fifo = dai->playback_dma_data;
1478c2ecf20Sopenharmony_ci	int ret;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	snd_soc_set_runtime_hwparams(substream, fifo->pcm);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	/*
1528c2ecf20Sopenharmony_ci	 * Make sure the buffer and period size are multiple of the fifo burst
1538c2ecf20Sopenharmony_ci	 * size
1548c2ecf20Sopenharmony_ci	 */
1558c2ecf20Sopenharmony_ci	ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
1568c2ecf20Sopenharmony_ci					 SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
1578c2ecf20Sopenharmony_ci					 fifo->fifo_block);
1588c2ecf20Sopenharmony_ci	if (ret)
1598c2ecf20Sopenharmony_ci		return ret;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
1628c2ecf20Sopenharmony_ci					 SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
1638c2ecf20Sopenharmony_ci					 fifo->fifo_block);
1648c2ecf20Sopenharmony_ci	if (ret)
1658c2ecf20Sopenharmony_ci		return ret;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(fifo->pclk);
1688c2ecf20Sopenharmony_ci	if (ret)
1698c2ecf20Sopenharmony_ci		return ret;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	ret = request_irq(fifo->irq, aiu_fifo_isr, 0, dev_name(dai->dev),
1728c2ecf20Sopenharmony_ci			  substream);
1738c2ecf20Sopenharmony_ci	if (ret)
1748c2ecf20Sopenharmony_ci		clk_disable_unprepare(fifo->pclk);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	return ret;
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_civoid aiu_fifo_shutdown(struct snd_pcm_substream *substream,
1808c2ecf20Sopenharmony_ci		       struct snd_soc_dai *dai)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	struct aiu_fifo *fifo = dai->playback_dma_data;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	free_irq(fifo->irq, substream);
1858c2ecf20Sopenharmony_ci	clk_disable_unprepare(fifo->pclk);
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ciint aiu_fifo_pcm_new(struct snd_soc_pcm_runtime *rtd,
1898c2ecf20Sopenharmony_ci		     struct snd_soc_dai *dai)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream =
1928c2ecf20Sopenharmony_ci		rtd->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
1938c2ecf20Sopenharmony_ci	struct snd_card *card = rtd->card->snd_card;
1948c2ecf20Sopenharmony_ci	struct aiu_fifo *fifo = dai->playback_dma_data;
1958c2ecf20Sopenharmony_ci	size_t size = fifo->pcm->buffer_bytes_max;
1968c2ecf20Sopenharmony_ci	int ret;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
1998c2ecf20Sopenharmony_ci	if (ret)
2008c2ecf20Sopenharmony_ci		return ret;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	snd_pcm_lib_preallocate_pages(substream,
2038c2ecf20Sopenharmony_ci				      SNDRV_DMA_TYPE_DEV,
2048c2ecf20Sopenharmony_ci				      card->dev, size, size);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	return 0;
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ciint aiu_fifo_dai_probe(struct snd_soc_dai *dai)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	struct aiu_fifo *fifo;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	fifo = kzalloc(sizeof(*fifo), GFP_KERNEL);
2148c2ecf20Sopenharmony_ci	if (!fifo)
2158c2ecf20Sopenharmony_ci		return -ENOMEM;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	dai->playback_dma_data = fifo;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	return 0;
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ciint aiu_fifo_dai_remove(struct snd_soc_dai *dai)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	kfree(dai->playback_dma_data);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	return 0;
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
229