Unverified Commit f74028e1 authored by Sameer Pujar's avatar Sameer Pujar Committed by Mark Brown
Browse files

ASoC: tegra: Add Tegra210 based ADMAIF driver



ADMAIF is the interface between ADMA and AHUB. Each ADMA channel that
sends/receives data to/from AHUB must intreface through an ADMAIF channel.
ADMA channel sending data to AHUB pairs with an ADMAIF Tx channel and
similarly ADMA channel receiving data from AHUB pairs with an ADMAIF Rx
channel. Buffer size is configurable for each ADMAIF channel, but currently
SW uses default values.

This patch registers ADMAIF driver with ASoC framework. The component
driver exposes DAPM widgets, routes and kcontrols for the device. The DAI
driver exposes ADMAIF interfaces, which can be used to connect different
components in the ASoC layer. Makefile and Kconfig support is added to
allow to build the driver. The ADMAIF device can be enabled in the DT via
"nvidia,tegra210-admaif" compatible binding.

Tegra PCM driver is updated to expose required PCM interfaces and
snd_pcm_ops callbacks.

Signed-off-by: default avatarSameer Pujar <spujar@nvidia.com>
Link: https://lore.kernel.org/r/1595134890-16470-8-git-send-email-spujar@nvidia.com


Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 327ef647
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -106,6 +106,18 @@ config SND_SOC_TEGRA186_DSPK
	  the desired 1-bit output via Delta Sigma Modulation (DSM).
	  Say Y or M if you want to add support for Tegra186 DSPK module.

config SND_SOC_TEGRA210_ADMAIF
	tristate "Tegra210 ADMAIF module"
	depends on SND_SOC_TEGRA
	help
	  Config to enable ADMAIF which is the interface between ADMA and
	  Audio Hub (AHUB). Each ADMA channel that sends/receives data to/
	  from AHUB must interface through an ADMAIF channel. ADMA channel
	  sending data to AHUB pairs with an ADMAIF Tx channel, where as
	  ADMA channel receiving data from AHUB pairs with an ADMAIF Rx
	  channel. Buffer size is configurable for each ADMAIIF channel.
	  Say Y or M if you want to add support for Tegra210 ADMAIF module.

config SND_SOC_TEGRA_RT5640
	tristate "SoC Audio support for Tegra boards using an RT5640 codec"
	depends on SND_SOC_TEGRA && I2C && GPIOLIB
+2 −0
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@ snd-soc-tegra210-ahub-objs := tegra210_ahub.o
snd-soc-tegra210-dmic-objs := tegra210_dmic.o
snd-soc-tegra210-i2s-objs := tegra210_i2s.o
snd-soc-tegra186-dspk-objs := tegra186_dspk.o
snd-soc-tegra210-admaif-objs := tegra210_admaif.o

obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o
obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-utils.o
@@ -25,6 +26,7 @@ obj-$(CONFIG_SND_SOC_TEGRA210_DMIC) += snd-soc-tegra210-dmic.o
obj-$(CONFIG_SND_SOC_TEGRA210_AHUB) += snd-soc-tegra210-ahub.o
obj-$(CONFIG_SND_SOC_TEGRA210_I2S) += snd-soc-tegra210-i2s.o
obj-$(CONFIG_SND_SOC_TEGRA186_DSPK) += snd-soc-tegra186-dspk.o
obj-$(CONFIG_SND_SOC_TEGRA210_ADMAIF) += snd-soc-tegra210-admaif.o

# Tegra machine Support
snd-soc-tegra-rt5640-objs := tegra_rt5640.o
+800 −0

File added.

Preview size limit exceeded, changes collapsed.

+162 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * tegra210_admaif.h - Tegra ADMAIF registers
 *
 * Copyright (c) 2020 NVIDIA CORPORATION.  All rights reserved.
 *
 */

#ifndef __TEGRA_ADMAIF_H__
#define __TEGRA_ADMAIF_H__

#define TEGRA_ADMAIF_CHANNEL_REG_STRIDE			0x40
/* Tegra210 specific */
#define TEGRA210_ADMAIF_LAST_REG			0x75f
#define TEGRA210_ADMAIF_CHANNEL_COUNT			10
#define TEGRA210_ADMAIF_RX_BASE				0x0
#define TEGRA210_ADMAIF_TX_BASE				0x300
#define TEGRA210_ADMAIF_GLOBAL_BASE			0x700
/* Tegra186 specific */
#define TEGRA186_ADMAIF_LAST_REG			0xd5f
#define TEGRA186_ADMAIF_CHANNEL_COUNT			20
#define TEGRA186_ADMAIF_RX_BASE				0x0
#define TEGRA186_ADMAIF_TX_BASE				0x500
#define TEGRA186_ADMAIF_GLOBAL_BASE			0xd00
/* Global registers */
#define TEGRA_ADMAIF_GLOBAL_ENABLE			0x0
#define TEGRA_ADMAIF_GLOBAL_CG_0			0x8
#define TEGRA_ADMAIF_GLOBAL_STATUS			0x10
#define TEGRA_ADMAIF_GLOBAL_RX_ENABLE_STATUS		0x20
#define TEGRA_ADMAIF_GLOBAL_TX_ENABLE_STATUS		0x24
/* RX channel registers */
#define TEGRA_ADMAIF_RX_ENABLE				0x0
#define TEGRA_ADMAIF_RX_SOFT_RESET			0x4
#define TEGRA_ADMAIF_RX_STATUS				0xc
#define TEGRA_ADMAIF_RX_INT_STATUS			0x10
#define TEGRA_ADMAIF_RX_INT_MASK			0x14
#define TEGRA_ADMAIF_RX_INT_SET				0x18
#define TEGRA_ADMAIF_RX_INT_CLEAR			0x1c
#define TEGRA_ADMAIF_CH_ACIF_RX_CTRL			0x20
#define TEGRA_ADMAIF_RX_FIFO_CTRL			0x28
#define TEGRA_ADMAIF_RX_FIFO_READ			0x2c
/* TX channel registers */
#define TEGRA_ADMAIF_TX_ENABLE				0x0
#define TEGRA_ADMAIF_TX_SOFT_RESET			0x4
#define TEGRA_ADMAIF_TX_STATUS				0xc
#define TEGRA_ADMAIF_TX_INT_STATUS			0x10
#define TEGRA_ADMAIF_TX_INT_MASK			0x14
#define TEGRA_ADMAIF_TX_INT_SET				0x18
#define TEGRA_ADMAIF_TX_INT_CLEAR			0x1c
#define TEGRA_ADMAIF_CH_ACIF_TX_CTRL			0x20
#define TEGRA_ADMAIF_TX_FIFO_CTRL			0x28
#define TEGRA_ADMAIF_TX_FIFO_WRITE			0x2c
/* Bit fields */
#define PACK8_EN_SHIFT					31
#define PACK8_EN_MASK					BIT(PACK8_EN_SHIFT)
#define PACK8_EN					BIT(PACK8_EN_SHIFT)
#define PACK16_EN_SHIFT					30
#define PACK16_EN_MASK					BIT(PACK16_EN_SHIFT)
#define PACK16_EN					BIT(PACK16_EN_SHIFT)
#define TX_ENABLE_SHIFT					0
#define TX_ENABLE_MASK					BIT(TX_ENABLE_SHIFT)
#define TX_ENABLE					BIT(TX_ENABLE_SHIFT)
#define RX_ENABLE_SHIFT					0
#define RX_ENABLE_MASK					BIT(RX_ENABLE_SHIFT)
#define RX_ENABLE					BIT(RX_ENABLE_SHIFT)
#define SW_RESET_MASK					1
#define SW_RESET					1
/* Default values - Tegra210 */
#define TEGRA210_ADMAIF_RX1_FIFO_CTRL_REG_DEFAULT	0x00000300
#define TEGRA210_ADMAIF_RX2_FIFO_CTRL_REG_DEFAULT	0x00000304
#define TEGRA210_ADMAIF_RX3_FIFO_CTRL_REG_DEFAULT	0x00000208
#define TEGRA210_ADMAIF_RX4_FIFO_CTRL_REG_DEFAULT	0x0000020b
#define TEGRA210_ADMAIF_RX5_FIFO_CTRL_REG_DEFAULT	0x0000020e
#define TEGRA210_ADMAIF_RX6_FIFO_CTRL_REG_DEFAULT	0x00000211
#define TEGRA210_ADMAIF_RX7_FIFO_CTRL_REG_DEFAULT	0x00000214
#define TEGRA210_ADMAIF_RX8_FIFO_CTRL_REG_DEFAULT	0x00000217
#define TEGRA210_ADMAIF_RX9_FIFO_CTRL_REG_DEFAULT	0x0000021a
#define TEGRA210_ADMAIF_RX10_FIFO_CTRL_REG_DEFAULT	0x0000021d
#define TEGRA210_ADMAIF_TX1_FIFO_CTRL_REG_DEFAULT	0x02000300
#define TEGRA210_ADMAIF_TX2_FIFO_CTRL_REG_DEFAULT	0x02000304
#define TEGRA210_ADMAIF_TX3_FIFO_CTRL_REG_DEFAULT	0x01800208
#define TEGRA210_ADMAIF_TX4_FIFO_CTRL_REG_DEFAULT	0x0180020b
#define TEGRA210_ADMAIF_TX5_FIFO_CTRL_REG_DEFAULT	0x0180020e
#define TEGRA210_ADMAIF_TX6_FIFO_CTRL_REG_DEFAULT	0x01800211
#define TEGRA210_ADMAIF_TX7_FIFO_CTRL_REG_DEFAULT	0x01800214
#define TEGRA210_ADMAIF_TX8_FIFO_CTRL_REG_DEFAULT	0x01800217
#define TEGRA210_ADMAIF_TX9_FIFO_CTRL_REG_DEFAULT	0x0180021a
#define TEGRA210_ADMAIF_TX10_FIFO_CTRL_REG_DEFAULT	0x0180021d
/* Default values - Tegra186 */
#define TEGRA186_ADMAIF_RX1_FIFO_CTRL_REG_DEFAULT	0x00000300
#define TEGRA186_ADMAIF_RX2_FIFO_CTRL_REG_DEFAULT	0x00000304
#define TEGRA186_ADMAIF_RX3_FIFO_CTRL_REG_DEFAULT	0x00000308
#define TEGRA186_ADMAIF_RX4_FIFO_CTRL_REG_DEFAULT	0x0000030c
#define TEGRA186_ADMAIF_RX5_FIFO_CTRL_REG_DEFAULT	0x00000210
#define TEGRA186_ADMAIF_RX6_FIFO_CTRL_REG_DEFAULT	0x00000213
#define TEGRA186_ADMAIF_RX7_FIFO_CTRL_REG_DEFAULT	0x00000216
#define TEGRA186_ADMAIF_RX8_FIFO_CTRL_REG_DEFAULT	0x00000219
#define TEGRA186_ADMAIF_RX9_FIFO_CTRL_REG_DEFAULT	0x0000021c
#define TEGRA186_ADMAIF_RX10_FIFO_CTRL_REG_DEFAULT	0x0000021f
#define TEGRA186_ADMAIF_RX11_FIFO_CTRL_REG_DEFAULT	0x00000222
#define TEGRA186_ADMAIF_RX12_FIFO_CTRL_REG_DEFAULT	0x00000225
#define TEGRA186_ADMAIF_RX13_FIFO_CTRL_REG_DEFAULT	0x00000228
#define TEGRA186_ADMAIF_RX14_FIFO_CTRL_REG_DEFAULT	0x0000022b
#define TEGRA186_ADMAIF_RX15_FIFO_CTRL_REG_DEFAULT	0x0000022e
#define TEGRA186_ADMAIF_RX16_FIFO_CTRL_REG_DEFAULT	0x00000231
#define TEGRA186_ADMAIF_RX17_FIFO_CTRL_REG_DEFAULT	0x00000234
#define TEGRA186_ADMAIF_RX18_FIFO_CTRL_REG_DEFAULT	0x00000237
#define TEGRA186_ADMAIF_RX19_FIFO_CTRL_REG_DEFAULT	0x0000023a
#define TEGRA186_ADMAIF_RX20_FIFO_CTRL_REG_DEFAULT	0x0000023d
#define TEGRA186_ADMAIF_TX1_FIFO_CTRL_REG_DEFAULT	0x02000300
#define TEGRA186_ADMAIF_TX2_FIFO_CTRL_REG_DEFAULT	0x02000304
#define TEGRA186_ADMAIF_TX3_FIFO_CTRL_REG_DEFAULT	0x02000308
#define TEGRA186_ADMAIF_TX4_FIFO_CTRL_REG_DEFAULT	0x0200030c
#define TEGRA186_ADMAIF_TX5_FIFO_CTRL_REG_DEFAULT	0x01800210
#define TEGRA186_ADMAIF_TX6_FIFO_CTRL_REG_DEFAULT	0x01800213
#define TEGRA186_ADMAIF_TX7_FIFO_CTRL_REG_DEFAULT	0x01800216
#define TEGRA186_ADMAIF_TX8_FIFO_CTRL_REG_DEFAULT	0x01800219
#define TEGRA186_ADMAIF_TX9_FIFO_CTRL_REG_DEFAULT	0x0180021c
#define TEGRA186_ADMAIF_TX10_FIFO_CTRL_REG_DEFAULT	0x0180021f
#define TEGRA186_ADMAIF_TX11_FIFO_CTRL_REG_DEFAULT	0x01800222
#define TEGRA186_ADMAIF_TX12_FIFO_CTRL_REG_DEFAULT	0x01800225
#define TEGRA186_ADMAIF_TX13_FIFO_CTRL_REG_DEFAULT	0x01800228
#define TEGRA186_ADMAIF_TX14_FIFO_CTRL_REG_DEFAULT	0x0180022b
#define TEGRA186_ADMAIF_TX15_FIFO_CTRL_REG_DEFAULT	0x0180022e
#define TEGRA186_ADMAIF_TX16_FIFO_CTRL_REG_DEFAULT	0x01800231
#define TEGRA186_ADMAIF_TX17_FIFO_CTRL_REG_DEFAULT	0x01800234
#define TEGRA186_ADMAIF_TX18_FIFO_CTRL_REG_DEFAULT	0x01800237
#define TEGRA186_ADMAIF_TX19_FIFO_CTRL_REG_DEFAULT	0x0180023a
#define TEGRA186_ADMAIF_TX20_FIFO_CTRL_REG_DEFAULT	0x0180023d

enum {
	DATA_8BIT,
	DATA_16BIT,
	DATA_32BIT
};

enum {
	ADMAIF_RX_PATH,
	ADMAIF_TX_PATH,
	ADMAIF_PATHS,
};

struct tegra_admaif_soc_data {
	const struct snd_soc_component_driver *cmpnt;
	const struct regmap_config *regmap_conf;
	struct snd_soc_dai_driver *dais;
	unsigned int global_base;
	unsigned int tx_base;
	unsigned int rx_base;
	unsigned int num_ch;
};

struct tegra_admaif {
	struct snd_dmaengine_dai_dma_data *capture_dma_data;
	struct snd_dmaengine_dai_dma_data *playback_dma_data;
	const struct tegra_admaif_soc_data *soc_data;
	unsigned int *mono_to_stereo[ADMAIF_PATHS];
	unsigned int *stereo_to_mono[ADMAIF_PATHS];
	struct regmap *regmap;
};

#endif
+234 −1
Original line number Diff line number Diff line
@@ -16,12 +16,12 @@
 */

#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/dmaengine_pcm.h>

#include "tegra_pcm.h"

static const struct snd_pcm_hardware tegra_pcm_hardware = {
@@ -67,6 +67,239 @@ void tegra_pcm_platform_unregister(struct device *dev)
}
EXPORT_SYMBOL_GPL(tegra_pcm_platform_unregister);

int tegra_pcm_open(struct snd_soc_component *component,
		   struct snd_pcm_substream *substream)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct snd_dmaengine_dai_dma_data *dmap;
	struct dma_chan *chan;
	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
	int ret;

	if (rtd->dai_link->no_pcm)
		return 0;

	dmap = snd_soc_dai_get_dma_data(cpu_dai, substream);

	/* Set HW params now that initialization is complete */
	snd_soc_set_runtime_hwparams(substream, &tegra_pcm_hardware);

	/* Ensure period size is multiple of 8 */
	ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
					 SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 0x8);
	if (ret) {
		dev_err(rtd->dev, "failed to set constraint %d\n", ret);
		return ret;
	}

	chan = dma_request_slave_channel(cpu_dai->dev, dmap->chan_name);
	if (!chan) {
		dev_err(cpu_dai->dev,
			"dmaengine request slave channel failed! (%s)\n",
			dmap->chan_name);
		return -ENODEV;
	}

	ret = snd_dmaengine_pcm_open(substream, chan);
	if (ret) {
		dev_err(rtd->dev,
			"dmaengine pcm open failed with err %d (%s)\n", ret,
			dmap->chan_name);

		dma_release_channel(chan);

		return ret;
	}

	return 0;
}
EXPORT_SYMBOL_GPL(tegra_pcm_open);

int tegra_pcm_close(struct snd_soc_component *component,
		    struct snd_pcm_substream *substream)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;

	if (rtd->dai_link->no_pcm)
		return 0;

	snd_dmaengine_pcm_close_release_chan(substream);

	return 0;
}
EXPORT_SYMBOL_GPL(tegra_pcm_close);

int tegra_pcm_hw_params(struct snd_soc_component *component,
			struct snd_pcm_substream *substream,
			struct snd_pcm_hw_params *params)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct snd_dmaengine_dai_dma_data *dmap;
	struct dma_slave_config slave_config;
	struct dma_chan *chan;
	int ret;

	if (rtd->dai_link->no_pcm)
		return 0;

	dmap = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
	if (!dmap)
		return 0;

	chan = snd_dmaengine_pcm_get_chan(substream);

	ret = snd_hwparams_to_dma_slave_config(substream, params,
					       &slave_config);
	if (ret) {
		dev_err(rtd->dev, "hw params config failed with err %d\n", ret);
		return ret;
	}

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
		slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
		slave_config.dst_addr = dmap->addr;
		slave_config.dst_maxburst = 8;
	} else {
		slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
		slave_config.src_addr = dmap->addr;
		slave_config.src_maxburst = 8;
	}

	ret = dmaengine_slave_config(chan, &slave_config);
	if (ret < 0) {
		dev_err(rtd->dev, "dma slave config failed with err %d\n", ret);
		return ret;
	}

	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);

	return 0;
}
EXPORT_SYMBOL_GPL(tegra_pcm_hw_params);

int tegra_pcm_hw_free(struct snd_soc_component *component,
		      struct snd_pcm_substream *substream)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;

	if (rtd->dai_link->no_pcm)
		return 0;

	snd_pcm_set_runtime_buffer(substream, NULL);

	return 0;
}
EXPORT_SYMBOL_GPL(tegra_pcm_hw_free);

int tegra_pcm_mmap(struct snd_soc_component *component,
		   struct snd_pcm_substream *substream,
		   struct vm_area_struct *vma)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct snd_pcm_runtime *runtime = substream->runtime;

	if (rtd->dai_link->no_pcm)
		return 0;

	return dma_mmap_wc(substream->pcm->card->dev, vma, runtime->dma_area,
			   runtime->dma_addr, runtime->dma_bytes);
}
EXPORT_SYMBOL_GPL(tegra_pcm_mmap);

snd_pcm_uframes_t tegra_pcm_pointer(struct snd_soc_component *component,
				    struct snd_pcm_substream *substream)
{
	return snd_dmaengine_pcm_pointer(substream);
}
EXPORT_SYMBOL_GPL(tegra_pcm_pointer);

static int tegra_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream,
					    size_t size)
{
	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
	struct snd_dma_buffer *buf = &substream->dma_buffer;

	buf->area = dma_alloc_wc(pcm->card->dev, size, &buf->addr, GFP_KERNEL);
	if (!buf->area)
		return -ENOMEM;

	buf->private_data = NULL;
	buf->dev.type = SNDRV_DMA_TYPE_DEV;
	buf->dev.dev = pcm->card->dev;
	buf->bytes = size;

	return 0;
}

static void tegra_pcm_deallocate_dma_buffer(struct snd_pcm *pcm, int stream)
{
	struct snd_pcm_substream *substream;
	struct snd_dma_buffer *buf;

	substream = pcm->streams[stream].substream;
	if (!substream)
		return;

	buf = &substream->dma_buffer;
	if (!buf->area)
		return;

	dma_free_wc(pcm->card->dev, buf->bytes, buf->area, buf->addr);
	buf->area = NULL;
}

static int tegra_pcm_dma_allocate(struct snd_soc_pcm_runtime *rtd,
				  size_t size)
{
	struct snd_card *card = rtd->card->snd_card;
	struct snd_pcm *pcm = rtd->pcm;
	int ret;

	ret = dma_set_mask(card->dev, DMA_BIT_MASK(32));
	if (ret < 0)
		return ret;

	ret = dma_set_coherent_mask(card->dev, DMA_BIT_MASK(32));
	if (ret < 0)
		return ret;

	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
		ret = tegra_pcm_preallocate_dma_buffer(pcm,
			SNDRV_PCM_STREAM_PLAYBACK, size);
		if (ret)
			goto err;
	}

	if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
		ret = tegra_pcm_preallocate_dma_buffer(pcm,
			SNDRV_PCM_STREAM_CAPTURE, size);
		if (ret)
			goto err_free_play;
	}

	return 0;

err_free_play:
	tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK);
err:
	return ret;
}

int tegra_pcm_construct(struct snd_soc_component *component,
			struct snd_soc_pcm_runtime *rtd)
{
	return tegra_pcm_dma_allocate(rtd, tegra_pcm_hardware.buffer_bytes_max);
}
EXPORT_SYMBOL_GPL(tegra_pcm_construct);

void tegra_pcm_destruct(struct snd_soc_component *component,
			struct snd_pcm *pcm)
{
	tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE);
	tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK);
}
EXPORT_SYMBOL_GPL(tegra_pcm_destruct);

MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
MODULE_DESCRIPTION("Tegra PCM ASoC driver");
MODULE_LICENSE("GPL");
Loading