From: Maruthi Srinivas Bayyavarapu <Maruthi.Bayyavarapu@xxxxxxx> PCM platform driver binds to the platform device creatd by ACP3x PCI device. PCM driver registers ALSA DMA and CPU DAI components with ASoC framework. Signed-off-by: Maruthi Bayyavarapu <maruthi.bayyavarapu at amd.com> Signed-off-by: Alex Deucher <alexander.deucher at amd.com> --- sound/soc/amd/raven/acp3x-pcm-dma.c | 278 ++++++++++++++++++++++++++++++++++++ sound/soc/amd/raven/acp3x.h | 3 + 2 files changed, 281 insertions(+) create mode 100644 sound/soc/amd/raven/acp3x-pcm-dma.c diff --git a/sound/soc/amd/raven/acp3x-pcm-dma.c b/sound/soc/amd/raven/acp3x-pcm-dma.c new file mode 100644 index 0000000..3fd9f86 --- /dev/null +++ b/sound/soc/amd/raven/acp3x-pcm-dma.c @@ -0,0 +1,278 @@ +/* + * AMD ALSA SoC PCM Driver + * + * Copyright 2016 Advanced Micro Devices, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/err.h> + +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include "acp3x.h" + +struct i2s_dev_data { + void __iomem *acp3x_base; + struct snd_pcm_substream *play_stream; + struct snd_pcm_substream *capture_stream; +}; + +static int acp3x_power_on(void __iomem *acp3x_base, bool on) +{ + u16 val, mask; + u32 timeout; + + if (on == true) { + val = 1; + mask = 0; + } else { + val = 0; + mask = 2; + } + + rv_writel(val, acp3x_base + mmACP_PGFSM_CONTROL); + timeout = 0; + while (true) { + val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS); + if ((val & 0x3) == mask) + break; + if (timeout > 100) { + pr_err("ACP3x power state change failure\n"); + return -ENODEV; + } + timeout++; + cpu_relax(); + } + return 0; +} + +static int acp3x_reset(void __iomem *acp3x_base) +{ + u32 val, timeout; + + rv_writel(1, acp3x_base + mmACP_SOFT_RESET); + timeout = 0; + while (true) { + val = rv_readl(acp3x_base + mmACP_SOFT_RESET); + if ((val & 0x00010001) || timeout > 100) { + if (val & 0x00010001) + break; + return -ENODEV; + } + timeout++; + cpu_relax(); + } + + rv_writel(0, acp3x_base + mmACP_SOFT_RESET); + timeout = 0; + while (true) { + val = rv_readl(acp3x_base + mmACP_SOFT_RESET); + if (!val || timeout > 100) { + if (!val) + break; + return -ENODEV; + } + timeout++; + cpu_relax(); + } + return 0; +} + +static int acp3x_init(void __iomem *acp3x_base) +{ + int ret; + + /* power on */ + ret = acp3x_power_on(acp3x_base, true); + if (ret) { + pr_err("ACP3x power on failed\n"); + return ret; + } + + /* Reset */ + ret = acp3x_reset(acp3x_base); + if (ret) { + pr_err("ACP3x reset failed\n"); + return ret; + } + + pr_info("ACP Initialized\n"); + return 0; +} + +static int acp3x_deinit(void __iomem *acp3x_base) +{ + int ret; + + /* Reset */ + ret = acp3x_reset(acp3x_base); + if (ret) { + pr_err("ACP3x reset failed\n"); + return ret; + } + + /* power off */ + ret = acp3x_power_on(acp3x_base, false); + if (ret) { + pr_err("ACP3x power off failed\n"); + return ret; + } + + pr_info("ACP De-Initialized\n"); + return 0; +} + +static struct snd_pcm_ops acp3x_dma_ops = { + .open = NULL, + .close = NULL, + .ioctl = NULL, + .hw_params = NULL, + .hw_free = NULL, + .pointer = NULL, + .mmap = NULL, +}; + +static struct snd_soc_platform_driver acp3x_asoc_platform = { + .ops = &acp3x_dma_ops, + .pcm_new = NULL, +}; + +struct snd_soc_dai_ops acp3x_dai_i2s_ops = { + .hw_params = NULL, + .trigger = NULL, + .set_fmt = NULL, +}; + +static struct snd_soc_dai_driver acp3x_i2s_dai_driver = { + .playback = { + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 8, + + .rate_min = 8000, + .rate_max = 96000, + }, + .capture = { + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &acp3x_dai_i2s_ops, +}; + +static const struct snd_soc_component_driver acp3x_i2s_component = { + .name = "acp3x_i2s", +}; + +static int acp3x_audio_probe(struct platform_device *pdev) +{ + int status; + struct resource *res; + struct i2s_dev_data *adata; + unsigned int irqflags; + + if (pdev->dev.platform_data == NULL) { + dev_err(&pdev->dev, "platform_data not retrieved\n"); + return -ENODEV; + } + irqflags = *((unsigned int *)(pdev->dev.platform_data)); + + adata = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dev_data), + GFP_KERNEL); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n"); + return -ENODEV; + } + + adata->acp3x_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + + adata->play_stream = NULL; + adata->capture_stream = NULL; + + dev_set_drvdata(&pdev->dev, adata); + /* Initialize ACP */ + status = acp3x_init(adata->acp3x_base); + if (status) + return -ENODEV; + + status = snd_soc_register_platform(&pdev->dev, &acp3x_asoc_platform); + if (status != 0) { + dev_err(&pdev->dev, "Fail to register ALSA platform device\n"); + goto dev_err; + } + + status = devm_snd_soc_register_component(&pdev->dev, + &acp3x_i2s_component, &acp3x_i2s_dai_driver, 1); + if (status != 0) { + dev_err(&pdev->dev, "Fail to register acp i2s dai\n"); + snd_soc_unregister_platform(&pdev->dev); + goto dev_err; + } + + return 0; +dev_err: + status = acp3x_deinit(adata->acp3x_base); + if (status) + dev_err(&pdev->dev, "ACP de-init failed\n"); + else + dev_info(&pdev->dev, "ACP de-initialized\n"); + /*ignore device status and return driver probe error*/ + return -ENODEV; +} + +static int acp3x_audio_remove(struct platform_device *pdev) +{ + int ret; + struct i2s_dev_data *adata = dev_get_drvdata(&pdev->dev); + + snd_soc_unregister_component(&pdev->dev); + snd_soc_unregister_platform(&pdev->dev); + + ret = acp3x_deinit(adata->acp3x_base); + if (ret) + dev_err(&pdev->dev, "ACP de-init failed\n"); + else + dev_info(&pdev->dev, "ACP de-initialized\n"); + + return 0; +} + +static struct platform_driver acp3x_dma_driver = { + .probe = acp3x_audio_probe, + .remove = acp3x_audio_remove, + .driver = { + .name = "acp3x_rv_i2s", + }, +}; + +module_platform_driver(acp3x_dma_driver); + +MODULE_AUTHOR("Maruthi.Bayyavarapu at amd.com"); +MODULE_DESCRIPTION("AMD ACP 3.x PCM Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:acp3x-i2s-audio"); diff --git a/sound/soc/amd/raven/acp3x.h b/sound/soc/amd/raven/acp3x.h index 1996faf..b6d4659 100644 --- a/sound/soc/amd/raven/acp3x.h +++ b/sound/soc/amd/raven/acp3x.h @@ -4,6 +4,9 @@ #define ACP3x_I2S_MODE 0 #define ACP3x_REG_START 0x1240000 #define ACP3x_REG_END 0x1250200 +#define BT_TX_THRESHOLD 26 +#define BT_RX_THRESHOLD 25 + static inline u32 rv_readl(void __iomem *base_addr) { -- 2.5.5