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

acp-platform.c (9204B)


      1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
      2//
      3// This file is provided under a dual BSD/GPLv2 license. When using or
      4// redistributing this file, you may do so under either license.
      5//
      6// Copyright(c) 2021 Advanced Micro Devices, Inc.
      7//
      8// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
      9
     10/*
     11 * Generic interface for ACP audio blck PCM component
     12 */
     13
     14#include <linux/platform_device.h>
     15#include <linux/module.h>
     16#include <linux/err.h>
     17#include <linux/io.h>
     18#include <sound/pcm_params.h>
     19#include <sound/soc.h>
     20#include <sound/soc-dai.h>
     21#include <linux/pm_runtime.h>
     22#include <linux/dma-mapping.h>
     23
     24#include "amd.h"
     25
     26#define DRV_NAME "acp_i2s_dma"
     27
     28static const struct snd_pcm_hardware acp_pcm_hardware_playback = {
     29	.info = SNDRV_PCM_INFO_INTERLEAVED |
     30		SNDRV_PCM_INFO_BLOCK_TRANSFER |
     31		SNDRV_PCM_INFO_BATCH |
     32		SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
     33		SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
     34	.formats = SNDRV_PCM_FMTBIT_S16_LE |  SNDRV_PCM_FMTBIT_S8 |
     35		   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE |
     36		   SNDRV_PCM_FMTBIT_S32_LE,
     37	.channels_min = 2,
     38	.channels_max = 8,
     39	.rates = SNDRV_PCM_RATE_8000_96000,
     40	.rate_min = 8000,
     41	.rate_max = 96000,
     42	.buffer_bytes_max = PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE,
     43	.period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE,
     44	.period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE,
     45	.periods_min = PLAYBACK_MIN_NUM_PERIODS,
     46	.periods_max = PLAYBACK_MAX_NUM_PERIODS,
     47};
     48
     49static const struct snd_pcm_hardware acp_pcm_hardware_capture = {
     50	.info = SNDRV_PCM_INFO_INTERLEAVED |
     51		SNDRV_PCM_INFO_BLOCK_TRANSFER |
     52		SNDRV_PCM_INFO_BATCH |
     53		SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
     54		SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
     55	.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
     56		   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE |
     57		   SNDRV_PCM_FMTBIT_S32_LE,
     58	.channels_min = 2,
     59	.channels_max = 2,
     60	.rates = SNDRV_PCM_RATE_8000_48000,
     61	.rate_min = 8000,
     62	.rate_max = 48000,
     63	.buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
     64	.period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
     65	.period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
     66	.periods_min = CAPTURE_MIN_NUM_PERIODS,
     67	.periods_max = CAPTURE_MAX_NUM_PERIODS,
     68};
     69
     70int acp_machine_select(struct acp_dev_data *adata)
     71{
     72	struct snd_soc_acpi_mach *mach;
     73	int size;
     74
     75	size = sizeof(*adata->machines);
     76	mach = snd_soc_acpi_find_machine(adata->machines);
     77	if (!mach) {
     78		dev_err(adata->dev, "warning: No matching ASoC machine driver found\n");
     79		return -EINVAL;
     80	}
     81
     82	adata->mach_dev = platform_device_register_data(adata->dev, mach->drv_name,
     83							PLATFORM_DEVID_NONE, mach, size);
     84	if (IS_ERR(adata->mach_dev))
     85		dev_warn(adata->dev, "Unable to register Machine device\n");
     86
     87	return 0;
     88}
     89EXPORT_SYMBOL_NS_GPL(acp_machine_select, SND_SOC_ACP_COMMON);
     90
     91static irqreturn_t i2s_irq_handler(int irq, void *data)
     92{
     93	struct acp_dev_data *adata = data;
     94	struct acp_stream *stream;
     95	u16 i2s_flag = 0;
     96	u32 val, i;
     97
     98	if (!adata)
     99		return IRQ_NONE;
    100
    101	val = readl(adata->acp_base + ACP_EXTERNAL_INTR_STAT);
    102
    103	for (i = 0; i < ACP_MAX_STREAM; i++) {
    104		stream = adata->stream[i];
    105		if (stream && (val & stream->irq_bit)) {
    106			writel(stream->irq_bit, adata->acp_base + ACP_EXTERNAL_INTR_STAT);
    107			snd_pcm_period_elapsed(stream->substream);
    108			i2s_flag = 1;
    109			break;
    110		}
    111	}
    112
    113	if (i2s_flag)
    114		return IRQ_HANDLED;
    115
    116	return IRQ_NONE;
    117}
    118
    119static void config_pte_for_stream(struct acp_dev_data *adata, struct acp_stream *stream)
    120{
    121	u32 pte_reg, pte_size, reg_val;
    122
    123	/* Use ATU base Group5 */
    124	pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_5;
    125	pte_size =  ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5;
    126	stream->reg_offset = 0x02000000;
    127
    128	/* Group Enable */
    129	reg_val = ACP_SRAM_PTE_OFFSET;
    130	writel(reg_val | BIT(31), adata->acp_base + pte_reg);
    131	writel(PAGE_SIZE_4K_ENABLE,  adata->acp_base + pte_size);
    132}
    133
    134static void config_acp_dma(struct acp_dev_data *adata, int cpu_id, int size)
    135{
    136	struct acp_stream *stream = adata->stream[cpu_id];
    137	struct snd_pcm_substream *substream = stream->substream;
    138	dma_addr_t addr = substream->dma_buffer.addr;
    139	int num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
    140	u32 low, high, val;
    141	u16 page_idx;
    142
    143	val = stream->pte_offset;
    144
    145	for (page_idx = 0; page_idx < num_pages; page_idx++) {
    146		/* Load the low address of page int ACP SRAM through SRBM */
    147		low = lower_32_bits(addr);
    148		high = upper_32_bits(addr);
    149		writel(low, adata->acp_base + ACP_SCRATCH_REG_0 + val);
    150		high |= BIT(31);
    151		writel(high, adata->acp_base + ACP_SCRATCH_REG_0 + val + 4);
    152
    153		/* Move to next physically contiguous page */
    154		val += 8;
    155		addr += PAGE_SIZE;
    156	}
    157}
    158
    159static int acp_dma_open(struct snd_soc_component *component, struct snd_pcm_substream *substream)
    160{
    161	struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
    162	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
    163	struct snd_pcm_runtime *runtime = substream->runtime;
    164	struct device *dev = component->dev;
    165	struct acp_dev_data *adata = dev_get_drvdata(dev);
    166	struct acp_stream *stream;
    167	int stream_id = cpu_dai->driver->id * 2 + substream->stream;
    168	int ret;
    169
    170	stream = kzalloc(sizeof(*stream), GFP_KERNEL);
    171	if (!stream)
    172		return -ENOMEM;
    173
    174	stream->substream = substream;
    175	adata->stream[stream_id] = stream;
    176
    177	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
    178		runtime->hw = acp_pcm_hardware_playback;
    179	else
    180		runtime->hw = acp_pcm_hardware_capture;
    181
    182	ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
    183	if (ret < 0) {
    184		dev_err(component->dev, "set integer constraint failed\n");
    185		kfree(stream);
    186		return ret;
    187	}
    188	runtime->private_data = stream;
    189
    190	writel(1, adata->acp_base + ACP_EXTERNAL_INTR_ENB);
    191
    192	return ret;
    193}
    194
    195static int acp_dma_hw_params(struct snd_soc_component *component,
    196			     struct snd_pcm_substream *substream,
    197			     struct snd_pcm_hw_params *params)
    198{
    199	struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
    200	struct acp_dev_data *adata = snd_soc_component_get_drvdata(component);
    201	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
    202	struct acp_stream *stream = substream->runtime->private_data;
    203	int stream_id = cpu_dai->driver->id * 2 + substream->stream;
    204	u64 size = params_buffer_bytes(params);
    205
    206	/* Configure ACP DMA block with params */
    207	config_pte_for_stream(adata, stream);
    208	config_acp_dma(adata, stream_id, size);
    209
    210	return 0;
    211}
    212
    213static snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component,
    214					 struct snd_pcm_substream *substream)
    215{
    216	struct device *dev = component->dev;
    217	struct acp_dev_data *adata = dev_get_drvdata(dev);
    218	struct acp_stream *stream = substream->runtime->private_data;
    219	u32 pos, buffersize;
    220	u64 bytescount;
    221
    222	buffersize = frames_to_bytes(substream->runtime,
    223				     substream->runtime->buffer_size);
    224
    225	bytescount = acp_get_byte_count(adata, stream->dai_id, substream->stream);
    226
    227	if (bytescount > stream->bytescount)
    228		bytescount -= stream->bytescount;
    229
    230	pos = do_div(bytescount, buffersize);
    231
    232	return bytes_to_frames(substream->runtime, pos);
    233}
    234
    235static int acp_dma_new(struct snd_soc_component *component,
    236		       struct snd_soc_pcm_runtime *rtd)
    237{
    238	struct device *parent = component->dev->parent;
    239
    240	snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
    241				       parent, MIN_BUFFER, MAX_BUFFER);
    242	return 0;
    243}
    244
    245static int acp_dma_mmap(struct snd_soc_component *component,
    246			struct snd_pcm_substream *substream,
    247			struct vm_area_struct *vma)
    248{
    249	return snd_pcm_lib_default_mmap(substream, vma);
    250}
    251
    252static int acp_dma_close(struct snd_soc_component *component,
    253			 struct snd_pcm_substream *substream)
    254{
    255	struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
    256	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
    257	struct device *dev = component->dev;
    258	struct acp_dev_data *adata = dev_get_drvdata(dev);
    259	struct acp_stream *stream;
    260	int stream_id = cpu_dai->driver->id * 2 + substream->stream;
    261
    262	stream = adata->stream[stream_id];
    263	kfree(stream);
    264	adata->stream[stream_id] = NULL;
    265
    266	return 0;
    267}
    268
    269static const struct snd_soc_component_driver acp_pcm_component = {
    270	.name		= DRV_NAME,
    271	.open		= acp_dma_open,
    272	.close		= acp_dma_close,
    273	.hw_params	= acp_dma_hw_params,
    274	.pointer	= acp_dma_pointer,
    275	.mmap		= acp_dma_mmap,
    276	.pcm_construct	= acp_dma_new,
    277};
    278
    279int acp_platform_register(struct device *dev)
    280{
    281	struct acp_dev_data *adata = dev_get_drvdata(dev);
    282	struct snd_soc_dai_driver;
    283	unsigned int status;
    284
    285	status = devm_request_irq(dev, adata->i2s_irq, i2s_irq_handler,
    286				  IRQF_SHARED, "ACP_I2S_IRQ", adata);
    287	if (status) {
    288		dev_err(dev, "ACP I2S IRQ request failed\n");
    289		return status;
    290	}
    291
    292	status = devm_snd_soc_register_component(dev, &acp_pcm_component,
    293						 adata->dai_driver,
    294						 adata->num_dai);
    295	if (status) {
    296		dev_err(dev, "Fail to register acp i2s component\n");
    297		return status;
    298	}
    299	return 0;
    300}
    301EXPORT_SYMBOL_NS_GPL(acp_platform_register, SND_SOC_ACP_COMMON);
    302
    303int acp_platform_unregister(struct device *dev)
    304{
    305	struct acp_dev_data *adata = dev_get_drvdata(dev);
    306
    307	if (adata->mach_dev)
    308		platform_device_unregister(adata->mach_dev);
    309	return 0;
    310}
    311EXPORT_SYMBOL_NS_GPL(acp_platform_unregister, SND_SOC_ACP_COMMON);
    312
    313MODULE_DESCRIPTION("AMD ACP PCM Driver");
    314MODULE_LICENSE("Dual BSD/GPL");
    315MODULE_ALIAS(DRV_NAME);