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

acp3x-pdm-dma.c (13034B)


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