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

acp6x-pdm-dma.c (12398B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * AMD ALSA SoC Yellow Carp PDM Driver
      4 *
      5 * Copyright 2021 Advanced Micro Devices, Inc.
      6 */
      7
      8#include <linux/platform_device.h>
      9#include <linux/module.h>
     10#include <linux/err.h>
     11#include <linux/io.h>
     12#include <sound/pcm_params.h>
     13#include <sound/soc.h>
     14#include <sound/soc-dai.h>
     15#include <linux/pm_runtime.h>
     16
     17#include "acp6x.h"
     18
     19#define DRV_NAME "acp_yc_pdm_dma"
     20
     21static const struct snd_pcm_hardware acp6x_pdm_hardware_capture = {
     22	.info = SNDRV_PCM_INFO_INTERLEAVED |
     23		SNDRV_PCM_INFO_BLOCK_TRANSFER |
     24		SNDRV_PCM_INFO_MMAP |
     25		SNDRV_PCM_INFO_MMAP_VALID |
     26		SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
     27	.formats = SNDRV_PCM_FMTBIT_S32_LE,
     28	.channels_min = 2,
     29	.channels_max = 2,
     30	.rates = SNDRV_PCM_RATE_48000,
     31	.rate_min = 48000,
     32	.rate_max = 48000,
     33	.buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
     34	.period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
     35	.period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
     36	.periods_min = CAPTURE_MIN_NUM_PERIODS,
     37	.periods_max = CAPTURE_MAX_NUM_PERIODS,
     38};
     39
     40static void acp6x_init_pdm_ring_buffer(u32 physical_addr, u32 buffer_size,
     41				       u32 watermark_size, void __iomem *acp_base)
     42{
     43	acp6x_writel(physical_addr, acp_base + ACP_WOV_RX_RINGBUFADDR);
     44	acp6x_writel(buffer_size, acp_base + ACP_WOV_RX_RINGBUFSIZE);
     45	acp6x_writel(watermark_size, acp_base + ACP_WOV_RX_INTR_WATERMARK_SIZE);
     46	acp6x_writel(0x01, acp_base + ACPAXI2AXI_ATU_CTRL);
     47}
     48
     49static void acp6x_enable_pdm_clock(void __iomem *acp_base)
     50{
     51	u32 pdm_clk_enable, pdm_ctrl;
     52
     53	pdm_clk_enable = ACP_PDM_CLK_FREQ_MASK;
     54	pdm_ctrl = 0x00;
     55
     56	acp6x_writel(pdm_clk_enable, acp_base + ACP_WOV_CLK_CTRL);
     57	pdm_ctrl = acp6x_readl(acp_base + ACP_WOV_MISC_CTRL);
     58	pdm_ctrl |= ACP_WOV_MISC_CTRL_MASK;
     59	acp6x_writel(pdm_ctrl, acp_base + ACP_WOV_MISC_CTRL);
     60}
     61
     62static void acp6x_enable_pdm_interrupts(void __iomem *acp_base)
     63{
     64	u32 ext_int_ctrl;
     65
     66	ext_int_ctrl = acp6x_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
     67	ext_int_ctrl |= PDM_DMA_INTR_MASK;
     68	acp6x_writel(ext_int_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
     69}
     70
     71static void acp6x_disable_pdm_interrupts(void __iomem *acp_base)
     72{
     73	u32 ext_int_ctrl;
     74
     75	ext_int_ctrl = acp6x_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
     76	ext_int_ctrl &= ~PDM_DMA_INTR_MASK;
     77	acp6x_writel(ext_int_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
     78}
     79
     80static bool acp6x_check_pdm_dma_status(void __iomem *acp_base)
     81{
     82	bool pdm_dma_status;
     83	u32 pdm_enable, pdm_dma_enable;
     84
     85	pdm_dma_status = false;
     86	pdm_enable = acp6x_readl(acp_base + ACP_WOV_PDM_ENABLE);
     87	pdm_dma_enable = acp6x_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
     88	if ((pdm_enable & ACP_PDM_ENABLE) && (pdm_dma_enable & ACP_PDM_DMA_EN_STATUS))
     89		pdm_dma_status = true;
     90
     91	return pdm_dma_status;
     92}
     93
     94static int acp6x_start_pdm_dma(void __iomem *acp_base)
     95{
     96	u32 pdm_enable;
     97	u32 pdm_dma_enable;
     98	int timeout;
     99
    100	pdm_enable = 0x01;
    101	pdm_dma_enable  = 0x01;
    102
    103	acp6x_enable_pdm_clock(acp_base);
    104	acp6x_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
    105	acp6x_writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
    106	timeout = 0;
    107	while (++timeout < ACP_COUNTER) {
    108		pdm_dma_enable = acp6x_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
    109		if ((pdm_dma_enable & 0x02) == ACP_PDM_DMA_EN_STATUS)
    110			return 0;
    111		udelay(DELAY_US);
    112	}
    113	return -ETIMEDOUT;
    114}
    115
    116static int acp6x_stop_pdm_dma(void __iomem *acp_base)
    117{
    118	u32 pdm_enable, pdm_dma_enable;
    119	int timeout;
    120
    121	pdm_enable = 0x00;
    122	pdm_dma_enable  = 0x00;
    123
    124	pdm_enable = acp6x_readl(acp_base + ACP_WOV_PDM_ENABLE);
    125	pdm_dma_enable = acp6x_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
    126	if (pdm_dma_enable & 0x01) {
    127		pdm_dma_enable = 0x02;
    128		acp6x_writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
    129		timeout = 0;
    130		while (++timeout < ACP_COUNTER) {
    131			pdm_dma_enable = acp6x_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
    132			if ((pdm_dma_enable & 0x02) == 0x00)
    133				break;
    134			udelay(DELAY_US);
    135		}
    136		if (timeout == ACP_COUNTER)
    137			return -ETIMEDOUT;
    138	}
    139	if (pdm_enable == ACP_PDM_ENABLE) {
    140		pdm_enable = ACP_PDM_DISABLE;
    141		acp6x_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
    142	}
    143	acp6x_writel(0x01, acp_base + ACP_WOV_PDM_FIFO_FLUSH);
    144	return 0;
    145}
    146
    147static void acp6x_config_dma(struct pdm_stream_instance *rtd, int direction)
    148{
    149	u16 page_idx;
    150	u32 low, high, val;
    151	dma_addr_t addr;
    152
    153	addr = rtd->dma_addr;
    154	val = PDM_PTE_OFFSET;
    155
    156	/* Group Enable */
    157	acp6x_writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp6x_base +
    158		     ACPAXI2AXI_ATU_BASE_ADDR_GRP_1);
    159	acp6x_writel(PAGE_SIZE_4K_ENABLE, rtd->acp6x_base +
    160		     ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1);
    161	for (page_idx = 0; page_idx < rtd->num_pages; page_idx++) {
    162		/* Load the low address of page int ACP SRAM through SRBM */
    163		low = lower_32_bits(addr);
    164		high = upper_32_bits(addr);
    165
    166		acp6x_writel(low, rtd->acp6x_base + ACP_SCRATCH_REG_0 + val);
    167		high |= BIT(31);
    168		acp6x_writel(high, rtd->acp6x_base + ACP_SCRATCH_REG_0 + val + 4);
    169		val += 8;
    170		addr += PAGE_SIZE;
    171	}
    172}
    173
    174static int acp6x_pdm_dma_open(struct snd_soc_component *component,
    175			      struct snd_pcm_substream *substream)
    176{
    177	struct snd_pcm_runtime *runtime;
    178	struct pdm_dev_data *adata;
    179	struct pdm_stream_instance *pdm_data;
    180	int ret;
    181
    182	runtime = substream->runtime;
    183	adata = dev_get_drvdata(component->dev);
    184	pdm_data = kzalloc(sizeof(*pdm_data), GFP_KERNEL);
    185	if (!pdm_data)
    186		return -EINVAL;
    187
    188	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
    189		runtime->hw = acp6x_pdm_hardware_capture;
    190
    191	ret = snd_pcm_hw_constraint_integer(runtime,
    192					    SNDRV_PCM_HW_PARAM_PERIODS);
    193	if (ret < 0) {
    194		dev_err(component->dev, "set integer constraint failed\n");
    195		kfree(pdm_data);
    196		return ret;
    197	}
    198
    199	acp6x_enable_pdm_interrupts(adata->acp6x_base);
    200
    201	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
    202		adata->capture_stream = substream;
    203
    204	pdm_data->acp6x_base = adata->acp6x_base;
    205	runtime->private_data = pdm_data;
    206	return ret;
    207}
    208
    209static int acp6x_pdm_dma_hw_params(struct snd_soc_component *component,
    210				   struct snd_pcm_substream *substream,
    211				   struct snd_pcm_hw_params *params)
    212{
    213	struct pdm_stream_instance *rtd;
    214	size_t size, period_bytes;
    215
    216	rtd = substream->runtime->private_data;
    217	if (!rtd)
    218		return -EINVAL;
    219	size = params_buffer_bytes(params);
    220	period_bytes = params_period_bytes(params);
    221	rtd->dma_addr = substream->runtime->dma_addr;
    222	rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
    223	acp6x_config_dma(rtd, substream->stream);
    224	acp6x_init_pdm_ring_buffer(PDM_MEM_WINDOW_START, size,
    225				   period_bytes, rtd->acp6x_base);
    226	return 0;
    227}
    228
    229static u64 acp6x_pdm_get_byte_count(struct pdm_stream_instance *rtd,
    230				    int direction)
    231{
    232	union acp_pdm_dma_count byte_count;
    233
    234	byte_count.bcount.high =
    235			acp6x_readl(rtd->acp6x_base + ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH);
    236	byte_count.bcount.low =
    237			acp6x_readl(rtd->acp6x_base + ACP_WOV_RX_LINEARPOSITIONCNTR_LOW);
    238	return byte_count.bytescount;
    239}
    240
    241static snd_pcm_uframes_t acp6x_pdm_dma_pointer(struct snd_soc_component *comp,
    242					       struct snd_pcm_substream *stream)
    243{
    244	struct pdm_stream_instance *rtd;
    245	u32 pos, buffersize;
    246	u64 bytescount;
    247
    248	rtd = stream->runtime->private_data;
    249	buffersize = frames_to_bytes(stream->runtime,
    250				     stream->runtime->buffer_size);
    251	bytescount = acp6x_pdm_get_byte_count(rtd, stream->stream);
    252	if (bytescount > rtd->bytescount)
    253		bytescount -= rtd->bytescount;
    254	pos = do_div(bytescount, buffersize);
    255	return bytes_to_frames(stream->runtime, pos);
    256}
    257
    258static int acp6x_pdm_dma_new(struct snd_soc_component *component,
    259			     struct snd_soc_pcm_runtime *rtd)
    260{
    261	struct device *parent = component->dev->parent;
    262
    263	snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
    264				       parent, MIN_BUFFER, MAX_BUFFER);
    265	return 0;
    266}
    267
    268static int acp6x_pdm_dma_close(struct snd_soc_component *component,
    269			       struct snd_pcm_substream *substream)
    270{
    271	struct pdm_dev_data *adata = dev_get_drvdata(component->dev);
    272
    273	acp6x_disable_pdm_interrupts(adata->acp6x_base);
    274	adata->capture_stream = NULL;
    275	return 0;
    276}
    277
    278static int acp6x_pdm_dai_trigger(struct snd_pcm_substream *substream,
    279				 int cmd, struct snd_soc_dai *dai)
    280{
    281	struct pdm_stream_instance *rtd;
    282	int ret;
    283	bool pdm_status;
    284	unsigned int ch_mask;
    285
    286	rtd = substream->runtime->private_data;
    287	ret = 0;
    288	switch (substream->runtime->channels) {
    289	case TWO_CH:
    290		ch_mask = 0x00;
    291		break;
    292	default:
    293		return -EINVAL;
    294	}
    295	switch (cmd) {
    296	case SNDRV_PCM_TRIGGER_START:
    297	case SNDRV_PCM_TRIGGER_RESUME:
    298	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
    299		acp6x_writel(ch_mask, rtd->acp6x_base + ACP_WOV_PDM_NO_OF_CHANNELS);
    300		acp6x_writel(PDM_DECIMATION_FACTOR, rtd->acp6x_base +
    301			     ACP_WOV_PDM_DECIMATION_FACTOR);
    302		rtd->bytescount = acp6x_pdm_get_byte_count(rtd, substream->stream);
    303		pdm_status = acp6x_check_pdm_dma_status(rtd->acp6x_base);
    304		if (!pdm_status)
    305			ret = acp6x_start_pdm_dma(rtd->acp6x_base);
    306		break;
    307	case SNDRV_PCM_TRIGGER_STOP:
    308	case SNDRV_PCM_TRIGGER_SUSPEND:
    309	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
    310		pdm_status = acp6x_check_pdm_dma_status(rtd->acp6x_base);
    311		if (pdm_status)
    312			ret = acp6x_stop_pdm_dma(rtd->acp6x_base);
    313		break;
    314	default:
    315		ret = -EINVAL;
    316		break;
    317	}
    318	return ret;
    319}
    320
    321static const struct snd_soc_dai_ops acp6x_pdm_dai_ops = {
    322	.trigger   = acp6x_pdm_dai_trigger,
    323};
    324
    325static struct snd_soc_dai_driver acp6x_pdm_dai_driver = {
    326	.capture = {
    327		.rates = SNDRV_PCM_RATE_48000,
    328		.formats = SNDRV_PCM_FMTBIT_S32_LE,
    329		.channels_min = 2,
    330		.channels_max = 2,
    331		.rate_min = 48000,
    332		.rate_max = 48000,
    333	},
    334	.ops = &acp6x_pdm_dai_ops,
    335};
    336
    337static const struct snd_soc_component_driver acp6x_pdm_component = {
    338	.name		= DRV_NAME,
    339	.open		= acp6x_pdm_dma_open,
    340	.close		= acp6x_pdm_dma_close,
    341	.hw_params	= acp6x_pdm_dma_hw_params,
    342	.pointer	= acp6x_pdm_dma_pointer,
    343	.pcm_construct	= acp6x_pdm_dma_new,
    344};
    345
    346static int acp6x_pdm_audio_probe(struct platform_device *pdev)
    347{
    348	struct resource *res;
    349	struct pdm_dev_data *adata;
    350	int status;
    351
    352	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    353	if (!res) {
    354		dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
    355		return -ENODEV;
    356	}
    357
    358	adata = devm_kzalloc(&pdev->dev, sizeof(*adata), GFP_KERNEL);
    359	if (!adata)
    360		return -ENOMEM;
    361
    362	adata->acp6x_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
    363	if (!adata->acp6x_base)
    364		return -ENOMEM;
    365
    366	adata->capture_stream = NULL;
    367
    368	dev_set_drvdata(&pdev->dev, adata);
    369	status = devm_snd_soc_register_component(&pdev->dev,
    370						 &acp6x_pdm_component,
    371						 &acp6x_pdm_dai_driver, 1);
    372	if (status) {
    373		dev_err(&pdev->dev, "Fail to register acp pdm dai\n");
    374
    375		return -ENODEV;
    376	}
    377	pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
    378	pm_runtime_use_autosuspend(&pdev->dev);
    379	pm_runtime_enable(&pdev->dev);
    380	pm_runtime_allow(&pdev->dev);
    381	return 0;
    382}
    383
    384static int acp6x_pdm_audio_remove(struct platform_device *pdev)
    385{
    386	pm_runtime_disable(&pdev->dev);
    387	return 0;
    388}
    389
    390static int __maybe_unused acp6x_pdm_resume(struct device *dev)
    391{
    392	struct pdm_dev_data *adata;
    393	struct snd_pcm_runtime *runtime;
    394	struct pdm_stream_instance *rtd;
    395	u32 period_bytes, buffer_len;
    396
    397	adata = dev_get_drvdata(dev);
    398	if (adata->capture_stream && adata->capture_stream->runtime) {
    399		runtime = adata->capture_stream->runtime;
    400		rtd = runtime->private_data;
    401		period_bytes = frames_to_bytes(runtime, runtime->period_size);
    402		buffer_len = frames_to_bytes(runtime, runtime->buffer_size);
    403		acp6x_config_dma(rtd, SNDRV_PCM_STREAM_CAPTURE);
    404		acp6x_init_pdm_ring_buffer(PDM_MEM_WINDOW_START, buffer_len,
    405					   period_bytes, adata->acp6x_base);
    406	}
    407	acp6x_enable_pdm_interrupts(adata->acp6x_base);
    408	return 0;
    409}
    410
    411static int __maybe_unused acp6x_pdm_suspend(struct device *dev)
    412{
    413	struct pdm_dev_data *adata;
    414
    415	adata = dev_get_drvdata(dev);
    416	acp6x_disable_pdm_interrupts(adata->acp6x_base);
    417	return 0;
    418}
    419
    420static int __maybe_unused acp6x_pdm_runtime_resume(struct device *dev)
    421{
    422	struct pdm_dev_data *adata;
    423
    424	adata = dev_get_drvdata(dev);
    425	acp6x_enable_pdm_interrupts(adata->acp6x_base);
    426	return 0;
    427}
    428
    429static const struct dev_pm_ops acp6x_pdm_pm_ops = {
    430	SET_RUNTIME_PM_OPS(acp6x_pdm_suspend, acp6x_pdm_runtime_resume, NULL)
    431	SET_SYSTEM_SLEEP_PM_OPS(acp6x_pdm_suspend, acp6x_pdm_resume)
    432};
    433
    434static struct platform_driver acp6x_pdm_dma_driver = {
    435	.probe = acp6x_pdm_audio_probe,
    436	.remove = acp6x_pdm_audio_remove,
    437	.driver = {
    438		.name = "acp_yc_pdm_dma",
    439		.pm = &acp6x_pdm_pm_ops,
    440	},
    441};
    442
    443module_platform_driver(acp6x_pdm_dma_driver);
    444
    445MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
    446MODULE_DESCRIPTION("AMD ACP6x YC PDM Driver");
    447MODULE_LICENSE("GPL v2");
    448MODULE_ALIAS("platform:" DRV_NAME);