cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

tegra_pcm.c (5975B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * tegra_pcm.c - Tegra PCM driver
      4 *
      5 * Author: Stephen Warren <swarren@nvidia.com>
      6 * Copyright (C) 2010,2012 - NVIDIA, Inc.
      7 *
      8 * Based on code copyright/by:
      9 *
     10 * Copyright (c) 2009-2010, NVIDIA Corporation.
     11 * Scott Peterson <speterson@nvidia.com>
     12 * Vijay Mali <vmali@nvidia.com>
     13 *
     14 * Copyright (C) 2010 Google, Inc.
     15 * Iliyan Malchev <malchev@google.com>
     16 */
     17
     18#include <linux/module.h>
     19#include <linux/dma-mapping.h>
     20#include <sound/core.h>
     21#include <sound/pcm.h>
     22#include <sound/pcm_params.h>
     23#include <sound/soc.h>
     24#include <sound/dmaengine_pcm.h>
     25#include "tegra_pcm.h"
     26
     27static const struct snd_pcm_hardware tegra_pcm_hardware = {
     28	.info			= SNDRV_PCM_INFO_MMAP |
     29				  SNDRV_PCM_INFO_MMAP_VALID |
     30				  SNDRV_PCM_INFO_INTERLEAVED,
     31	.period_bytes_min	= 1024,
     32	.period_bytes_max	= PAGE_SIZE,
     33	.periods_min		= 2,
     34	.periods_max		= 8,
     35	.buffer_bytes_max	= PAGE_SIZE * 8,
     36	.fifo_size		= 4,
     37};
     38
     39static const struct snd_dmaengine_pcm_config tegra_dmaengine_pcm_config = {
     40	.pcm_hardware = &tegra_pcm_hardware,
     41	.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
     42	.prealloc_buffer_size = PAGE_SIZE * 8,
     43};
     44
     45int tegra_pcm_platform_register(struct device *dev)
     46{
     47	return snd_dmaengine_pcm_register(dev, &tegra_dmaengine_pcm_config, 0);
     48}
     49EXPORT_SYMBOL_GPL(tegra_pcm_platform_register);
     50
     51int devm_tegra_pcm_platform_register(struct device *dev)
     52{
     53	return devm_snd_dmaengine_pcm_register(dev, &tegra_dmaengine_pcm_config, 0);
     54}
     55EXPORT_SYMBOL_GPL(devm_tegra_pcm_platform_register);
     56
     57int tegra_pcm_platform_register_with_chan_names(struct device *dev,
     58				struct snd_dmaengine_pcm_config *config,
     59				char *txdmachan, char *rxdmachan)
     60{
     61	*config = tegra_dmaengine_pcm_config;
     62	config->dma_dev = dev->parent;
     63	config->chan_names[0] = txdmachan;
     64	config->chan_names[1] = rxdmachan;
     65
     66	return snd_dmaengine_pcm_register(dev, config, 0);
     67}
     68EXPORT_SYMBOL_GPL(tegra_pcm_platform_register_with_chan_names);
     69
     70void tegra_pcm_platform_unregister(struct device *dev)
     71{
     72	return snd_dmaengine_pcm_unregister(dev);
     73}
     74EXPORT_SYMBOL_GPL(tegra_pcm_platform_unregister);
     75
     76int tegra_pcm_open(struct snd_soc_component *component,
     77		   struct snd_pcm_substream *substream)
     78{
     79	struct snd_soc_pcm_runtime *rtd = substream->private_data;
     80	struct snd_dmaengine_dai_dma_data *dmap;
     81	struct dma_chan *chan;
     82	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
     83	int ret;
     84
     85	if (rtd->dai_link->no_pcm)
     86		return 0;
     87
     88	dmap = snd_soc_dai_get_dma_data(cpu_dai, substream);
     89
     90	/* Set HW params now that initialization is complete */
     91	snd_soc_set_runtime_hwparams(substream, &tegra_pcm_hardware);
     92
     93	/* Ensure period size is multiple of 8 */
     94	ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
     95					 SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 0x8);
     96	if (ret) {
     97		dev_err(rtd->dev, "failed to set constraint %d\n", ret);
     98		return ret;
     99	}
    100
    101	chan = dma_request_slave_channel(cpu_dai->dev, dmap->chan_name);
    102	if (!chan) {
    103		dev_err(cpu_dai->dev,
    104			"dmaengine request slave channel failed! (%s)\n",
    105			dmap->chan_name);
    106		return -ENODEV;
    107	}
    108
    109	ret = snd_dmaengine_pcm_open(substream, chan);
    110	if (ret) {
    111		dev_err(rtd->dev,
    112			"dmaengine pcm open failed with err %d (%s)\n", ret,
    113			dmap->chan_name);
    114
    115		dma_release_channel(chan);
    116
    117		return ret;
    118	}
    119
    120	return 0;
    121}
    122EXPORT_SYMBOL_GPL(tegra_pcm_open);
    123
    124int tegra_pcm_close(struct snd_soc_component *component,
    125		    struct snd_pcm_substream *substream)
    126{
    127	struct snd_soc_pcm_runtime *rtd = substream->private_data;
    128
    129	if (rtd->dai_link->no_pcm)
    130		return 0;
    131
    132	snd_dmaengine_pcm_close_release_chan(substream);
    133
    134	return 0;
    135}
    136EXPORT_SYMBOL_GPL(tegra_pcm_close);
    137
    138int tegra_pcm_hw_params(struct snd_soc_component *component,
    139			struct snd_pcm_substream *substream,
    140			struct snd_pcm_hw_params *params)
    141{
    142	struct snd_soc_pcm_runtime *rtd = substream->private_data;
    143	struct snd_dmaengine_dai_dma_data *dmap;
    144	struct dma_slave_config slave_config;
    145	struct dma_chan *chan;
    146	int ret;
    147
    148	if (rtd->dai_link->no_pcm)
    149		return 0;
    150
    151	dmap = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
    152	if (!dmap)
    153		return 0;
    154
    155	chan = snd_dmaengine_pcm_get_chan(substream);
    156
    157	ret = snd_hwparams_to_dma_slave_config(substream, params,
    158					       &slave_config);
    159	if (ret) {
    160		dev_err(rtd->dev, "hw params config failed with err %d\n", ret);
    161		return ret;
    162	}
    163
    164	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
    165		slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
    166		slave_config.dst_addr = dmap->addr;
    167		slave_config.dst_maxburst = 8;
    168	} else {
    169		slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
    170		slave_config.src_addr = dmap->addr;
    171		slave_config.src_maxburst = 8;
    172	}
    173
    174	ret = dmaengine_slave_config(chan, &slave_config);
    175	if (ret < 0) {
    176		dev_err(rtd->dev, "dma slave config failed with err %d\n", ret);
    177		return ret;
    178	}
    179
    180	return 0;
    181}
    182EXPORT_SYMBOL_GPL(tegra_pcm_hw_params);
    183
    184snd_pcm_uframes_t tegra_pcm_pointer(struct snd_soc_component *component,
    185				    struct snd_pcm_substream *substream)
    186{
    187	return snd_dmaengine_pcm_pointer(substream);
    188}
    189EXPORT_SYMBOL_GPL(tegra_pcm_pointer);
    190
    191static int tegra_pcm_dma_allocate(struct device *dev, struct snd_soc_pcm_runtime *rtd,
    192				  size_t size)
    193{
    194	struct snd_pcm *pcm = rtd->pcm;
    195	int ret;
    196
    197	ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
    198	if (ret < 0)
    199		return ret;
    200
    201	return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_WC, dev, size);
    202}
    203
    204int tegra_pcm_construct(struct snd_soc_component *component,
    205			struct snd_soc_pcm_runtime *rtd)
    206{
    207	struct device *dev = component->dev;
    208
    209	/*
    210	 * Fallback for backwards-compatibility with older device trees that
    211	 * have the iommus property in the virtual, top-level "sound" node.
    212	 */
    213	if (!of_get_property(dev->of_node, "iommus", NULL))
    214		dev = rtd->card->snd_card->dev;
    215
    216	return tegra_pcm_dma_allocate(dev, rtd, tegra_pcm_hardware.buffer_bytes_max);
    217}
    218EXPORT_SYMBOL_GPL(tegra_pcm_construct);
    219
    220MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
    221MODULE_DESCRIPTION("Tegra PCM ASoC driver");
    222MODULE_LICENSE("GPL");