18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci//
38c2ecf20Sopenharmony_ci// Socionext UniPhier AIO DMA driver.
48c2ecf20Sopenharmony_ci//
58c2ecf20Sopenharmony_ci// Copyright (c) 2016-2018 Socionext Inc.
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
88c2ecf20Sopenharmony_ci#include <linux/errno.h>
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <sound/core.h>
128c2ecf20Sopenharmony_ci#include <sound/pcm.h>
138c2ecf20Sopenharmony_ci#include <sound/soc.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include "aio.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistatic struct snd_pcm_hardware uniphier_aiodma_hw = {
188c2ecf20Sopenharmony_ci	.info = SNDRV_PCM_INFO_MMAP |
198c2ecf20Sopenharmony_ci		SNDRV_PCM_INFO_MMAP_VALID |
208c2ecf20Sopenharmony_ci		SNDRV_PCM_INFO_INTERLEAVED,
218c2ecf20Sopenharmony_ci	.period_bytes_min = 256,
228c2ecf20Sopenharmony_ci	.period_bytes_max = 4096,
238c2ecf20Sopenharmony_ci	.periods_min      = 4,
248c2ecf20Sopenharmony_ci	.periods_max      = 1024,
258c2ecf20Sopenharmony_ci	.buffer_bytes_max = 128 * 1024,
268c2ecf20Sopenharmony_ci};
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic void aiodma_pcm_irq(struct uniphier_aio_sub *sub)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = sub->substream->runtime;
318c2ecf20Sopenharmony_ci	int bytes = runtime->period_size *
328c2ecf20Sopenharmony_ci		runtime->channels * samples_to_bytes(runtime, 1);
338c2ecf20Sopenharmony_ci	int ret;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	spin_lock(&sub->lock);
368c2ecf20Sopenharmony_ci	ret = aiodma_rb_set_threshold(sub, runtime->dma_bytes,
378c2ecf20Sopenharmony_ci				      sub->threshold + bytes);
388c2ecf20Sopenharmony_ci	if (!ret)
398c2ecf20Sopenharmony_ci		sub->threshold += bytes;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	aiodma_rb_sync(sub, runtime->dma_addr, runtime->dma_bytes, bytes);
428c2ecf20Sopenharmony_ci	aiodma_rb_clear_irq(sub);
438c2ecf20Sopenharmony_ci	spin_unlock(&sub->lock);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	snd_pcm_period_elapsed(sub->substream);
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic void aiodma_compr_irq(struct uniphier_aio_sub *sub)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	struct snd_compr_runtime *runtime = sub->cstream->runtime;
518c2ecf20Sopenharmony_ci	int bytes = runtime->fragment_size;
528c2ecf20Sopenharmony_ci	int ret;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	spin_lock(&sub->lock);
558c2ecf20Sopenharmony_ci	ret = aiodma_rb_set_threshold(sub, sub->compr_bytes,
568c2ecf20Sopenharmony_ci				      sub->threshold + bytes);
578c2ecf20Sopenharmony_ci	if (!ret)
588c2ecf20Sopenharmony_ci		sub->threshold += bytes;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	aiodma_rb_sync(sub, sub->compr_addr, sub->compr_bytes, bytes);
618c2ecf20Sopenharmony_ci	aiodma_rb_clear_irq(sub);
628c2ecf20Sopenharmony_ci	spin_unlock(&sub->lock);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	snd_compr_fragment_elapsed(sub->cstream);
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic irqreturn_t aiodma_irq(int irq, void *p)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	struct platform_device *pdev = p;
708c2ecf20Sopenharmony_ci	struct uniphier_aio_chip *chip = platform_get_drvdata(pdev);
718c2ecf20Sopenharmony_ci	irqreturn_t ret = IRQ_NONE;
728c2ecf20Sopenharmony_ci	int i, j;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	for (i = 0; i < chip->num_aios; i++) {
758c2ecf20Sopenharmony_ci		struct uniphier_aio *aio = &chip->aios[i];
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci		for (j = 0; j < ARRAY_SIZE(aio->sub); j++) {
788c2ecf20Sopenharmony_ci			struct uniphier_aio_sub *sub = &aio->sub[j];
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci			/* Skip channel that does not trigger */
818c2ecf20Sopenharmony_ci			if (!sub->running || !aiodma_rb_is_irq(sub))
828c2ecf20Sopenharmony_ci				continue;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci			if (sub->substream)
858c2ecf20Sopenharmony_ci				aiodma_pcm_irq(sub);
868c2ecf20Sopenharmony_ci			if (sub->cstream)
878c2ecf20Sopenharmony_ci				aiodma_compr_irq(sub);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci			ret = IRQ_HANDLED;
908c2ecf20Sopenharmony_ci		}
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	return ret;
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic int uniphier_aiodma_open(struct snd_soc_component *component,
978c2ecf20Sopenharmony_ci				struct snd_pcm_substream *substream)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	snd_soc_set_runtime_hwparams(substream, &uniphier_aiodma_hw);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	return snd_pcm_hw_constraint_step(runtime, 0,
1048c2ecf20Sopenharmony_ci		SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256);
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic int uniphier_aiodma_prepare(struct snd_soc_component *component,
1088c2ecf20Sopenharmony_ci				   struct snd_pcm_substream *substream)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
1118c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
1128c2ecf20Sopenharmony_ci	struct uniphier_aio *aio = uniphier_priv(asoc_rtd_to_cpu(rtd, 0));
1138c2ecf20Sopenharmony_ci	struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
1148c2ecf20Sopenharmony_ci	int bytes = runtime->period_size *
1158c2ecf20Sopenharmony_ci		runtime->channels * samples_to_bytes(runtime, 1);
1168c2ecf20Sopenharmony_ci	unsigned long flags;
1178c2ecf20Sopenharmony_ci	int ret;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	ret = aiodma_ch_set_param(sub);
1208c2ecf20Sopenharmony_ci	if (ret)
1218c2ecf20Sopenharmony_ci		return ret;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	spin_lock_irqsave(&sub->lock, flags);
1248c2ecf20Sopenharmony_ci	ret = aiodma_rb_set_buffer(sub, runtime->dma_addr,
1258c2ecf20Sopenharmony_ci				   runtime->dma_addr + runtime->dma_bytes,
1268c2ecf20Sopenharmony_ci				   bytes);
1278c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&sub->lock, flags);
1288c2ecf20Sopenharmony_ci	if (ret)
1298c2ecf20Sopenharmony_ci		return ret;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	return 0;
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic int uniphier_aiodma_trigger(struct snd_soc_component *component,
1358c2ecf20Sopenharmony_ci				   struct snd_pcm_substream *substream, int cmd)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
1388c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
1398c2ecf20Sopenharmony_ci	struct uniphier_aio *aio = uniphier_priv(asoc_rtd_to_cpu(rtd, 0));
1408c2ecf20Sopenharmony_ci	struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
1418c2ecf20Sopenharmony_ci	struct device *dev = &aio->chip->pdev->dev;
1428c2ecf20Sopenharmony_ci	int bytes = runtime->period_size *
1438c2ecf20Sopenharmony_ci		runtime->channels * samples_to_bytes(runtime, 1);
1448c2ecf20Sopenharmony_ci	unsigned long flags;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	spin_lock_irqsave(&sub->lock, flags);
1478c2ecf20Sopenharmony_ci	switch (cmd) {
1488c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
1498c2ecf20Sopenharmony_ci		aiodma_rb_sync(sub, runtime->dma_addr, runtime->dma_bytes,
1508c2ecf20Sopenharmony_ci			       bytes);
1518c2ecf20Sopenharmony_ci		aiodma_ch_set_enable(sub, 1);
1528c2ecf20Sopenharmony_ci		sub->running = 1;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci		break;
1558c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
1568c2ecf20Sopenharmony_ci		sub->running = 0;
1578c2ecf20Sopenharmony_ci		aiodma_ch_set_enable(sub, 0);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci		break;
1608c2ecf20Sopenharmony_ci	default:
1618c2ecf20Sopenharmony_ci		dev_warn(dev, "Unknown trigger(%d) ignored\n", cmd);
1628c2ecf20Sopenharmony_ci		break;
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&sub->lock, flags);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	return 0;
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t uniphier_aiodma_pointer(
1708c2ecf20Sopenharmony_ci					struct snd_soc_component *component,
1718c2ecf20Sopenharmony_ci					struct snd_pcm_substream *substream)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
1748c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
1758c2ecf20Sopenharmony_ci	struct uniphier_aio *aio = uniphier_priv(asoc_rtd_to_cpu(rtd, 0));
1768c2ecf20Sopenharmony_ci	struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
1778c2ecf20Sopenharmony_ci	int bytes = runtime->period_size *
1788c2ecf20Sopenharmony_ci		runtime->channels * samples_to_bytes(runtime, 1);
1798c2ecf20Sopenharmony_ci	unsigned long flags;
1808c2ecf20Sopenharmony_ci	snd_pcm_uframes_t pos;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	spin_lock_irqsave(&sub->lock, flags);
1838c2ecf20Sopenharmony_ci	aiodma_rb_sync(sub, runtime->dma_addr, runtime->dma_bytes, bytes);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	if (sub->swm->dir == PORT_DIR_OUTPUT)
1868c2ecf20Sopenharmony_ci		pos = bytes_to_frames(runtime, sub->rd_offs);
1878c2ecf20Sopenharmony_ci	else
1888c2ecf20Sopenharmony_ci		pos = bytes_to_frames(runtime, sub->wr_offs);
1898c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&sub->lock, flags);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	return pos;
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic int uniphier_aiodma_mmap(struct snd_soc_component *component,
1958c2ecf20Sopenharmony_ci				struct snd_pcm_substream *substream,
1968c2ecf20Sopenharmony_ci				struct vm_area_struct *vma)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	return remap_pfn_range(vma, vma->vm_start,
2018c2ecf20Sopenharmony_ci			       substream->runtime->dma_addr >> PAGE_SHIFT,
2028c2ecf20Sopenharmony_ci			       vma->vm_end - vma->vm_start, vma->vm_page_prot);
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic int uniphier_aiodma_new(struct snd_soc_component *component,
2068c2ecf20Sopenharmony_ci			       struct snd_soc_pcm_runtime *rtd)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	struct device *dev = rtd->card->snd_card->dev;
2098c2ecf20Sopenharmony_ci	struct snd_pcm *pcm = rtd->pcm;
2108c2ecf20Sopenharmony_ci	int ret;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(33));
2138c2ecf20Sopenharmony_ci	if (ret)
2148c2ecf20Sopenharmony_ci		return ret;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	snd_pcm_set_managed_buffer_all(pcm,
2178c2ecf20Sopenharmony_ci		SNDRV_DMA_TYPE_DEV, dev,
2188c2ecf20Sopenharmony_ci		uniphier_aiodma_hw.buffer_bytes_max,
2198c2ecf20Sopenharmony_ci		uniphier_aiodma_hw.buffer_bytes_max);
2208c2ecf20Sopenharmony_ci	return 0;
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver uniphier_soc_platform = {
2248c2ecf20Sopenharmony_ci	.open		= uniphier_aiodma_open,
2258c2ecf20Sopenharmony_ci	.prepare	= uniphier_aiodma_prepare,
2268c2ecf20Sopenharmony_ci	.trigger	= uniphier_aiodma_trigger,
2278c2ecf20Sopenharmony_ci	.pointer	= uniphier_aiodma_pointer,
2288c2ecf20Sopenharmony_ci	.mmap		= uniphier_aiodma_mmap,
2298c2ecf20Sopenharmony_ci	.pcm_construct	= uniphier_aiodma_new,
2308c2ecf20Sopenharmony_ci	.compress_ops	= &uniphier_aio_compress_ops,
2318c2ecf20Sopenharmony_ci};
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cistatic const struct regmap_config aiodma_regmap_config = {
2348c2ecf20Sopenharmony_ci	.reg_bits      = 32,
2358c2ecf20Sopenharmony_ci	.reg_stride    = 4,
2368c2ecf20Sopenharmony_ci	.val_bits      = 32,
2378c2ecf20Sopenharmony_ci	.max_register  = 0x7fffc,
2388c2ecf20Sopenharmony_ci	.cache_type    = REGCACHE_NONE,
2398c2ecf20Sopenharmony_ci};
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci/**
2428c2ecf20Sopenharmony_ci * uniphier_aiodma_soc_register_platform - register the AIO DMA
2438c2ecf20Sopenharmony_ci * @pdev: the platform device
2448c2ecf20Sopenharmony_ci *
2458c2ecf20Sopenharmony_ci * Register and setup the DMA of AIO to transfer the sound data to device.
2468c2ecf20Sopenharmony_ci * This function need to call once at driver startup and need NOT to call
2478c2ecf20Sopenharmony_ci * unregister function.
2488c2ecf20Sopenharmony_ci *
2498c2ecf20Sopenharmony_ci * Return: Zero if successful, otherwise a negative value on error.
2508c2ecf20Sopenharmony_ci */
2518c2ecf20Sopenharmony_ciint uniphier_aiodma_soc_register_platform(struct platform_device *pdev)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	struct uniphier_aio_chip *chip = platform_get_drvdata(pdev);
2548c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
2558c2ecf20Sopenharmony_ci	void __iomem *preg;
2568c2ecf20Sopenharmony_ci	int irq, ret;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	preg = devm_platform_ioremap_resource(pdev, 0);
2598c2ecf20Sopenharmony_ci	if (IS_ERR(preg))
2608c2ecf20Sopenharmony_ci		return PTR_ERR(preg);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	chip->regmap = devm_regmap_init_mmio(dev, preg,
2638c2ecf20Sopenharmony_ci					     &aiodma_regmap_config);
2648c2ecf20Sopenharmony_ci	if (IS_ERR(chip->regmap))
2658c2ecf20Sopenharmony_ci		return PTR_ERR(chip->regmap);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
2688c2ecf20Sopenharmony_ci	if (irq < 0)
2698c2ecf20Sopenharmony_ci		return irq;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	ret = devm_request_irq(dev, irq, aiodma_irq,
2728c2ecf20Sopenharmony_ci			       IRQF_SHARED, dev_name(dev), pdev);
2738c2ecf20Sopenharmony_ci	if (ret)
2748c2ecf20Sopenharmony_ci		return ret;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	return devm_snd_soc_register_component(dev, &uniphier_soc_platform,
2778c2ecf20Sopenharmony_ci					       NULL, 0);
2788c2ecf20Sopenharmony_ci}
2798c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(uniphier_aiodma_soc_register_platform);
280