brintos

brintos / linux-shallow public Read only

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