Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+47
View File
@@ -0,0 +1,47 @@
# SPDX-License-Identifier: GPL-2.0-only
menu "STMicroelectronics STM32"
config SND_SOC_STM32_SAI
tristate "STM32 SAI interface (Serial Audio Interface) support"
depends on (ARCH_STM32 && OF) || COMPILE_TEST
depends on COMMON_CLK
depends on SND_SOC
select SND_SOC_GENERIC_DMAENGINE_PCM
select REGMAP_MMIO
select SND_PCM_IEC958
help
Say Y if you want to enable SAI for STM32
config SND_SOC_STM32_I2S
tristate "STM32 I2S interface (SPI/I2S block) support"
depends on (ARCH_STM32 && OF) || COMPILE_TEST
depends on COMMON_CLK
depends on SND_SOC
select SND_SOC_GENERIC_DMAENGINE_PCM
select REGMAP_MMIO
help
Say Y if you want to enable I2S for STM32
config SND_SOC_STM32_SPDIFRX
tristate "STM32 S/PDIF receiver (SPDIFRX) support"
depends on (ARCH_STM32 && OF) || COMPILE_TEST
depends on SND_SOC
select SND_SOC_GENERIC_DMAENGINE_PCM
select REGMAP_MMIO
select SND_SOC_SPDIF
help
Say Y if you want to enable S/PDIF capture for STM32
config SND_SOC_STM32_DFSDM
tristate "SoC Audio support for STM32 DFSDM"
depends on ARCH_STM32 || COMPILE_TEST
depends on SND_SOC
depends on STM32_DFSDM_ADC
select SND_SOC_GENERIC_DMAENGINE_PCM
select SND_SOC_DMIC
select IIO_BUFFER_CB
help
Select this option to enable the STM32 Digital Filter
for Sigma Delta Modulators (DFSDM) driver used
in various STM32 series for digital microphone capture.
endmenu
+18
View File
@@ -0,0 +1,18 @@
# SPDX-License-Identifier: GPL-2.0
# SAI
snd-soc-stm32-sai-sub-y := stm32_sai_sub.o
obj-$(CONFIG_SND_SOC_STM32_SAI) += snd-soc-stm32-sai-sub.o
snd-soc-stm32-sai-y := stm32_sai.o
obj-$(CONFIG_SND_SOC_STM32_SAI) += snd-soc-stm32-sai.o
# I2S
snd-soc-stm32-i2s-y := stm32_i2s.o
obj-$(CONFIG_SND_SOC_STM32_I2S) += snd-soc-stm32-i2s.o
# SPDIFRX
snd-soc-stm32-spdifrx-y := stm32_spdifrx.o
obj-$(CONFIG_SND_SOC_STM32_SPDIFRX) += snd-soc-stm32-spdifrx.o
#DFSDM
obj-$(CONFIG_SND_SOC_STM32_DFSDM) += stm32_adfsdm.o
+394
View File
@@ -0,0 +1,394 @@
// SPDX-License-Identifier: GPL-2.0
/*
* This file is part of STM32 DFSDM ASoC DAI driver
*
* Copyright (C) 2017, STMicroelectronics - All Rights Reserved
* Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com>
* Olivier Moysan <olivier.moysan@st.com>
*/
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
#include <linux/iio/iio.h>
#include <linux/iio/consumer.h>
#include <linux/iio/adc/stm32-dfsdm-adc.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#define STM32_ADFSDM_DRV_NAME "stm32-adfsdm"
#define DFSDM_MAX_PERIOD_SIZE (PAGE_SIZE / 2)
#define DFSDM_MAX_PERIODS 6
struct stm32_adfsdm_priv {
struct snd_soc_dai_driver dai_drv;
struct snd_pcm_substream *substream;
struct device *dev;
/* IIO */
struct iio_channel *iio_ch;
struct iio_cb_buffer *iio_cb;
bool iio_active;
/* PCM buffer */
unsigned char *pcm_buff;
unsigned int pos;
struct mutex lock; /* protect against race condition on iio state */
};
static const struct snd_pcm_hardware stm32_adfsdm_pcm_hw = {
.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_PAUSE,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 1,
.channels_max = 1,
.periods_min = 2,
.periods_max = DFSDM_MAX_PERIODS,
.period_bytes_max = DFSDM_MAX_PERIOD_SIZE,
.buffer_bytes_max = DFSDM_MAX_PERIODS * DFSDM_MAX_PERIOD_SIZE
};
static void stm32_adfsdm_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
guard(mutex)(&priv->lock);
if (priv->iio_active) {
iio_channel_stop_all_cb(priv->iio_cb);
priv->iio_active = false;
}
}
static int stm32_adfsdm_dai_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
int ret;
guard(mutex)(&priv->lock);
if (priv->iio_active) {
iio_channel_stop_all_cb(priv->iio_cb);
priv->iio_active = false;
}
ret = iio_write_channel_attribute(priv->iio_ch,
substream->runtime->rate, 0,
IIO_CHAN_INFO_SAMP_FREQ);
if (ret < 0) {
dev_err(dai->dev, "%s: Failed to set %d sampling rate\n",
__func__, substream->runtime->rate);
return ret;
}
if (!priv->iio_active) {
ret = iio_channel_start_all_cb(priv->iio_cb);
if (!ret)
priv->iio_active = true;
else
dev_err(dai->dev, "%s: IIO channel start failed (%d)\n",
__func__, ret);
}
return ret;
}
static int stm32_adfsdm_set_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir)
{
struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
ssize_t size;
char str_freq[10];
dev_dbg(dai->dev, "%s: Enter for freq %d\n", __func__, freq);
/* Set IIO frequency if CODEC is master as clock comes from SPI_IN */
snprintf(str_freq, sizeof(str_freq), "%u\n", freq);
size = iio_write_channel_ext_info(priv->iio_ch, "spi_clk_freq",
str_freq, sizeof(str_freq));
if (size != sizeof(str_freq)) {
dev_err(dai->dev, "%s: Failed to set SPI clock\n",
__func__);
return -EINVAL;
}
return 0;
}
static const struct snd_soc_dai_ops stm32_adfsdm_dai_ops = {
.shutdown = stm32_adfsdm_shutdown,
.prepare = stm32_adfsdm_dai_prepare,
.set_sysclk = stm32_adfsdm_set_sysclk,
};
static const struct snd_soc_dai_driver stm32_adfsdm_dai = {
.capture = {
.channels_min = 1,
.channels_max = 1,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_CONTINUOUS,
.rate_min = 8000,
.rate_max = 192000,
},
.ops = &stm32_adfsdm_dai_ops,
};
static const struct snd_soc_component_driver stm32_adfsdm_dai_component = {
.name = "stm32_dfsdm_audio",
.legacy_dai_naming = 1,
};
static void stm32_memcpy_32to16(void *dest, const void *src, size_t n)
{
unsigned int i = 0;
u16 *d = (u16 *)dest, *s = (u16 *)src;
s++;
for (i = n >> 1; i > 0; i--) {
*d++ = *s++;
s++;
}
}
static int stm32_afsdm_pcm_cb(const void *data, size_t size, void *private)
{
struct stm32_adfsdm_priv *priv = private;
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(priv->substream);
u8 *pcm_buff = priv->pcm_buff;
u8 *src_buff = (u8 *)data;
unsigned int old_pos = priv->pos;
size_t buff_size = snd_pcm_lib_buffer_bytes(priv->substream);
size_t period_size = snd_pcm_lib_period_bytes(priv->substream);
size_t cur_size, src_size = size;
snd_pcm_format_t format = priv->substream->runtime->format;
if (format == SNDRV_PCM_FORMAT_S16_LE)
src_size >>= 1;
cur_size = src_size;
dev_dbg(rtd->dev, "%s: buff_add :%p, pos = %d, size = %zu\n",
__func__, &pcm_buff[priv->pos], priv->pos, src_size);
if ((priv->pos + src_size) > buff_size) {
if (format == SNDRV_PCM_FORMAT_S16_LE)
stm32_memcpy_32to16(&pcm_buff[priv->pos], src_buff,
buff_size - priv->pos);
else
memcpy(&pcm_buff[priv->pos], src_buff,
buff_size - priv->pos);
cur_size -= buff_size - priv->pos;
priv->pos = 0;
}
if (format == SNDRV_PCM_FORMAT_S16_LE)
stm32_memcpy_32to16(&pcm_buff[priv->pos],
&src_buff[src_size - cur_size], cur_size);
else
memcpy(&pcm_buff[priv->pos], &src_buff[src_size - cur_size],
cur_size);
priv->pos = (priv->pos + cur_size) % buff_size;
if (cur_size != src_size || (old_pos && (old_pos % period_size < size)))
snd_pcm_period_elapsed(priv->substream);
return 0;
}
static int stm32_adfsdm_trigger(struct snd_soc_component *component,
struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct stm32_adfsdm_priv *priv =
snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
priv->pos = 0;
return stm32_dfsdm_get_buff_cb(priv->iio_ch->indio_dev,
stm32_afsdm_pcm_cb, priv);
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
return stm32_dfsdm_release_buff_cb(priv->iio_ch->indio_dev);
}
return -EINVAL;
}
static int stm32_adfsdm_pcm_open(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
int ret;
ret = snd_soc_set_runtime_hwparams(substream, &stm32_adfsdm_pcm_hw);
if (!ret)
priv->substream = substream;
return ret;
}
static int stm32_adfsdm_pcm_close(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct stm32_adfsdm_priv *priv =
snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
priv->substream = NULL;
return 0;
}
static snd_pcm_uframes_t stm32_adfsdm_pcm_pointer(
struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct stm32_adfsdm_priv *priv =
snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
return bytes_to_frames(substream->runtime, priv->pos);
}
static int stm32_adfsdm_pcm_hw_params(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct stm32_adfsdm_priv *priv =
snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
priv->pcm_buff = substream->runtime->dma_area;
return iio_channel_cb_set_buffer_watermark(priv->iio_cb,
params_period_size(params));
}
static int stm32_adfsdm_pcm_new(struct snd_soc_component *component,
struct snd_soc_pcm_runtime *rtd)
{
struct snd_pcm *pcm = rtd->pcm;
struct stm32_adfsdm_priv *priv =
snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
unsigned int size = DFSDM_MAX_PERIODS * DFSDM_MAX_PERIOD_SIZE;
snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
priv->dev, size, size);
return 0;
}
static int stm32_adfsdm_dummy_cb(const void *data, void *private)
{
/*
* This dummy callback is requested by iio_channel_get_all_cb() API,
* but the stm32_dfsdm_get_buff_cb() API is used instead, to optimize
* DMA transfers.
*/
return 0;
}
static void stm32_adfsdm_cleanup(void *data)
{
iio_channel_release_all_cb(data);
}
static const struct snd_soc_component_driver stm32_adfsdm_soc_platform = {
.open = stm32_adfsdm_pcm_open,
.close = stm32_adfsdm_pcm_close,
.hw_params = stm32_adfsdm_pcm_hw_params,
.trigger = stm32_adfsdm_trigger,
.pointer = stm32_adfsdm_pcm_pointer,
.pcm_new = stm32_adfsdm_pcm_new,
.debugfs_prefix = "pcm",
};
static const struct of_device_id stm32_adfsdm_of_match[] = {
{.compatible = "st,stm32h7-dfsdm-dai"},
{}
};
MODULE_DEVICE_TABLE(of, stm32_adfsdm_of_match);
static int stm32_adfsdm_probe(struct platform_device *pdev)
{
struct stm32_adfsdm_priv *priv;
int ret;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = &pdev->dev;
priv->dai_drv = stm32_adfsdm_dai;
mutex_init(&priv->lock);
dev_set_drvdata(&pdev->dev, priv);
ret = devm_snd_soc_register_component(&pdev->dev,
&stm32_adfsdm_dai_component,
&priv->dai_drv, 1);
if (ret < 0)
return ret;
/* Associate iio channel */
priv->iio_ch = devm_iio_channel_get_all(&pdev->dev);
if (IS_ERR(priv->iio_ch))
return PTR_ERR(priv->iio_ch);
priv->iio_cb = iio_channel_get_all_cb(&pdev->dev, &stm32_adfsdm_dummy_cb, NULL);
if (IS_ERR(priv->iio_cb))
return PTR_ERR(priv->iio_cb);
ret = devm_add_action_or_reset(&pdev->dev, stm32_adfsdm_cleanup, priv->iio_cb);
if (ret < 0) {
dev_err(&pdev->dev, "Unable to add action\n");
return ret;
}
ret = devm_snd_soc_register_component(&pdev->dev,
&stm32_adfsdm_soc_platform,
NULL, 0);
if (ret < 0) {
dev_err(&pdev->dev, "%s: Failed to register PCM platform\n",
__func__);
return ret;
}
pm_runtime_enable(&pdev->dev);
return ret;
}
static void stm32_adfsdm_remove(struct platform_device *pdev)
{
snd_soc_unregister_component(&pdev->dev);
pm_runtime_disable(&pdev->dev);
}
static struct platform_driver stm32_adfsdm_driver = {
.driver = {
.name = STM32_ADFSDM_DRV_NAME,
.of_match_table = stm32_adfsdm_of_match,
},
.probe = stm32_adfsdm_probe,
.remove = stm32_adfsdm_remove,
};
module_platform_driver(stm32_adfsdm_driver);
MODULE_DESCRIPTION("stm32 DFSDM DAI driver");
MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" STM32_ADFSDM_DRV_NAME);
File diff suppressed because it is too large Load Diff
+316
View File
@@ -0,0 +1,316 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* STM32 ALSA SoC Digital Audio Interface (SAI) driver.
*
* Copyright (C) 2016, STMicroelectronics - All Rights Reserved
* Author(s): Olivier Moysan <olivier.moysan@st.com> for STMicroelectronics.
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/pinctrl/consumer.h>
#include <linux/reset.h>
#include <sound/dmaengine_pcm.h>
#include <sound/core.h>
#include "stm32_sai.h"
static int stm32_sai_get_parent_clk(struct stm32_sai_data *sai);
static const struct stm32_sai_conf stm32_sai_conf_f4 = {
.version = STM_SAI_STM32F4,
.fifo_size = 8,
.has_spdif_pdm = false,
.get_sai_ck_parent = stm32_sai_get_parent_clk,
};
/*
* Default settings for STM32H7x socs and STM32MP1x.
* These default settings will be overridden if the soc provides
* support of hardware configuration registers.
* - STM32H7: rely on default settings
* - STM32MP1: retrieve settings from registers
*/
static const struct stm32_sai_conf stm32_sai_conf_h7 = {
.version = STM_SAI_STM32H7,
.fifo_size = 8,
.has_spdif_pdm = true,
.get_sai_ck_parent = stm32_sai_get_parent_clk,
};
/*
* STM32MP2x:
* - do not use SAI parent clock source selection
* - do not use DMA burst mode
*/
static const struct stm32_sai_conf stm32_sai_conf_mp25 = {
.no_dma_burst = true,
};
static const struct of_device_id stm32_sai_ids[] = {
{ .compatible = "st,stm32f4-sai", .data = (void *)&stm32_sai_conf_f4 },
{ .compatible = "st,stm32h7-sai", .data = (void *)&stm32_sai_conf_h7 },
{ .compatible = "st,stm32mp25-sai", .data = (void *)&stm32_sai_conf_mp25 },
{}
};
static int stm32_sai_pclk_disable(struct device *dev)
{
struct stm32_sai_data *sai = dev_get_drvdata(dev);
clk_disable_unprepare(sai->pclk);
return 0;
}
static int stm32_sai_pclk_enable(struct device *dev)
{
struct stm32_sai_data *sai = dev_get_drvdata(dev);
int ret;
ret = clk_prepare_enable(sai->pclk);
if (ret) {
dev_err(&sai->pdev->dev, "failed to enable clock: %d\n", ret);
return ret;
}
return 0;
}
static int stm32_sai_sync_conf_client(struct stm32_sai_data *sai, int synci)
{
int ret;
/* Enable peripheral clock to allow GCR register access */
ret = stm32_sai_pclk_enable(&sai->pdev->dev);
if (ret)
return ret;
writel_relaxed(FIELD_PREP(SAI_GCR_SYNCIN_MASK, (synci - 1)), sai->base);
stm32_sai_pclk_disable(&sai->pdev->dev);
return 0;
}
static int stm32_sai_sync_conf_provider(struct stm32_sai_data *sai, int synco)
{
u32 prev_synco;
int ret;
/* Enable peripheral clock to allow GCR register access */
ret = stm32_sai_pclk_enable(&sai->pdev->dev);
if (ret)
return ret;
dev_dbg(&sai->pdev->dev, "Set %pOFn%s as synchro provider\n",
sai->pdev->dev.of_node,
synco == STM_SAI_SYNC_OUT_A ? "A" : "B");
prev_synco = FIELD_GET(SAI_GCR_SYNCOUT_MASK, readl_relaxed(sai->base));
if (prev_synco != STM_SAI_SYNC_OUT_NONE && synco != prev_synco) {
dev_err(&sai->pdev->dev, "%pOFn%s already set as sync provider\n",
sai->pdev->dev.of_node,
prev_synco == STM_SAI_SYNC_OUT_A ? "A" : "B");
stm32_sai_pclk_disable(&sai->pdev->dev);
return -EINVAL;
}
writel_relaxed(FIELD_PREP(SAI_GCR_SYNCOUT_MASK, synco), sai->base);
stm32_sai_pclk_disable(&sai->pdev->dev);
return 0;
}
static int stm32_sai_set_sync(struct stm32_sai_data *sai_client,
struct device_node *np_provider,
int synco, int synci)
{
struct platform_device *pdev = of_find_device_by_node(np_provider);
struct stm32_sai_data *sai_provider;
int ret;
if (!pdev) {
dev_err(&sai_client->pdev->dev,
"Device not found for node %pOFn\n", np_provider);
return -ENODEV;
}
sai_provider = platform_get_drvdata(pdev);
put_device(&pdev->dev);
if (!sai_provider) {
dev_err(&sai_client->pdev->dev,
"SAI sync provider data not found\n");
return -EINVAL;
}
/* Configure sync client */
ret = stm32_sai_sync_conf_client(sai_client, synci);
if (ret < 0)
return ret;
/* Configure sync provider */
return stm32_sai_sync_conf_provider(sai_provider, synco);
}
static int stm32_sai_get_parent_clk(struct stm32_sai_data *sai)
{
struct device *dev = &sai->pdev->dev;
sai->clk_x8k = devm_clk_get(dev, "x8k");
if (IS_ERR(sai->clk_x8k))
return dev_err_probe(dev, PTR_ERR(sai->clk_x8k),
"missing x8k parent clock\n");
sai->clk_x11k = devm_clk_get(dev, "x11k");
if (IS_ERR(sai->clk_x11k))
return dev_err_probe(dev, PTR_ERR(sai->clk_x11k),
"missing x11k parent clock\n");
return 0;
}
static int stm32_sai_probe(struct platform_device *pdev)
{
struct stm32_sai_data *sai;
const struct stm32_sai_conf *conf;
struct reset_control *rst;
u32 val;
int ret;
sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
if (!sai)
return -ENOMEM;
sai->pdev = pdev;
sai->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(sai->base))
return PTR_ERR(sai->base);
conf = device_get_match_data(&pdev->dev);
if (conf)
memcpy(&sai->conf, (const struct stm32_sai_conf *)conf,
sizeof(struct stm32_sai_conf));
else
return -EINVAL;
if (!STM_SAI_IS_F4(sai)) {
sai->pclk = devm_clk_get(&pdev->dev, "pclk");
if (IS_ERR(sai->pclk))
return dev_err_probe(&pdev->dev, PTR_ERR(sai->pclk),
"missing bus clock pclk\n");
}
if (sai->conf.get_sai_ck_parent) {
ret = sai->conf.get_sai_ck_parent(sai);
if (ret)
return ret;
}
/* init irqs */
sai->irq = platform_get_irq(pdev, 0);
if (sai->irq < 0)
return sai->irq;
/* reset */
rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL);
if (IS_ERR(rst))
return dev_err_probe(&pdev->dev, PTR_ERR(rst),
"Reset controller error\n");
reset_control_assert(rst);
udelay(2);
reset_control_deassert(rst);
/* Enable peripheral clock to allow register access */
ret = clk_prepare_enable(sai->pclk);
if (ret) {
dev_err(&pdev->dev, "failed to enable clock: %d\n", ret);
return ret;
}
val = FIELD_GET(SAI_IDR_ID_MASK,
readl_relaxed(sai->base + STM_SAI_IDR));
if (val == SAI_IPIDR_NUMBER) {
val = readl_relaxed(sai->base + STM_SAI_HWCFGR);
sai->conf.fifo_size = FIELD_GET(SAI_HWCFGR_FIFO_SIZE, val);
sai->conf.has_spdif_pdm = !!FIELD_GET(SAI_HWCFGR_SPDIF_PDM,
val);
val = readl_relaxed(sai->base + STM_SAI_VERR);
sai->conf.version = val;
dev_dbg(&pdev->dev, "SAI version: %lu.%lu registered\n",
FIELD_GET(SAI_VERR_MAJ_MASK, val),
FIELD_GET(SAI_VERR_MIN_MASK, val));
}
clk_disable_unprepare(sai->pclk);
sai->set_sync = &stm32_sai_set_sync;
platform_set_drvdata(pdev, sai);
return devm_of_platform_populate(&pdev->dev);
}
/*
* When pins are shared by two sai sub instances, pins have to be defined
* in sai parent node. In this case, pins state is not managed by alsa fw.
* These pins are managed in suspend/resume callbacks.
*/
static int stm32_sai_suspend(struct device *dev)
{
struct stm32_sai_data *sai = dev_get_drvdata(dev);
int ret;
ret = stm32_sai_pclk_enable(dev);
if (ret)
return ret;
sai->gcr = readl_relaxed(sai->base);
stm32_sai_pclk_disable(dev);
return pinctrl_pm_select_sleep_state(dev);
}
static int stm32_sai_resume(struct device *dev)
{
struct stm32_sai_data *sai = dev_get_drvdata(dev);
int ret;
ret = stm32_sai_pclk_enable(dev);
if (ret)
return ret;
writel_relaxed(sai->gcr, sai->base);
stm32_sai_pclk_disable(dev);
return pinctrl_pm_select_default_state(dev);
}
static const struct dev_pm_ops stm32_sai_pm_ops = {
SYSTEM_SLEEP_PM_OPS(stm32_sai_suspend, stm32_sai_resume)
};
MODULE_DEVICE_TABLE(of, stm32_sai_ids);
static struct platform_driver stm32_sai_driver = {
.driver = {
.name = "st,stm32-sai",
.of_match_table = stm32_sai_ids,
.pm = pm_ptr(&stm32_sai_pm_ops),
},
.probe = stm32_sai_probe,
};
module_platform_driver(stm32_sai_driver);
MODULE_DESCRIPTION("STM32 Soc SAI Interface");
MODULE_AUTHOR("Olivier Moysan <olivier.moysan@st.com>");
MODULE_ALIAS("platform:st,stm32-sai");
MODULE_LICENSE("GPL v2");
+308
View File
@@ -0,0 +1,308 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* STM32 ALSA SoC Digital Audio Interface (SAI) driver.
*
* Copyright (C) 2016, STMicroelectronics - All Rights Reserved
* Author(s): Olivier Moysan <olivier.moysan@st.com> for STMicroelectronics.
*/
#include <linux/bitfield.h>
/******************** SAI Register Map **************************************/
/* Global configuration register */
#define STM_SAI_GCR 0x00
/* Sub-block A&B registers offsets, relative to A&B sub-block addresses */
#define STM_SAI_CR1_REGX 0x00 /* A offset: 0x04. B offset: 0x24 */
#define STM_SAI_CR2_REGX 0x04
#define STM_SAI_FRCR_REGX 0x08
#define STM_SAI_SLOTR_REGX 0x0C
#define STM_SAI_IMR_REGX 0x10
#define STM_SAI_SR_REGX 0x14
#define STM_SAI_CLRFR_REGX 0x18
#define STM_SAI_DR_REGX 0x1C
/* Sub-block A registers, relative to sub-block A address */
#define STM_SAI_PDMCR_REGX 0x40
#define STM_SAI_PDMLY_REGX 0x44
/* Hardware configuration registers */
#define STM_SAI_HWCFGR 0x3F0
#define STM_SAI_VERR 0x3F4
#define STM_SAI_IDR 0x3F8
#define STM_SAI_SIDR 0x3FC
/******************** Bit definition for SAI_GCR register *******************/
#define SAI_GCR_SYNCIN_SHIFT 0
#define SAI_GCR_SYNCIN_WDTH 2
#define SAI_GCR_SYNCIN_MASK GENMASK(1, SAI_GCR_SYNCIN_SHIFT)
#define SAI_GCR_SYNCIN_MAX FIELD_GET(SAI_GCR_SYNCIN_MASK,\
SAI_GCR_SYNCIN_MASK)
#define SAI_GCR_SYNCOUT_SHIFT 4
#define SAI_GCR_SYNCOUT_MASK GENMASK(5, SAI_GCR_SYNCOUT_SHIFT)
/******************* Bit definition for SAI_XCR1 register *******************/
#define SAI_XCR1_RX_TX_SHIFT 0
#define SAI_XCR1_RX_TX BIT(SAI_XCR1_RX_TX_SHIFT)
#define SAI_XCR1_SLAVE_SHIFT 1
#define SAI_XCR1_SLAVE BIT(SAI_XCR1_SLAVE_SHIFT)
#define SAI_XCR1_PRTCFG_SHIFT 2
#define SAI_XCR1_PRTCFG_MASK GENMASK(3, SAI_XCR1_PRTCFG_SHIFT)
#define SAI_XCR1_PRTCFG_SET(x) ((x) << SAI_XCR1_PRTCFG_SHIFT)
#define SAI_XCR1_DS_SHIFT 5
#define SAI_XCR1_DS_MASK GENMASK(7, SAI_XCR1_DS_SHIFT)
#define SAI_XCR1_DS_SET(x) ((x) << SAI_XCR1_DS_SHIFT)
#define SAI_XCR1_LSBFIRST_SHIFT 8
#define SAI_XCR1_LSBFIRST BIT(SAI_XCR1_LSBFIRST_SHIFT)
#define SAI_XCR1_CKSTR_SHIFT 9
#define SAI_XCR1_CKSTR BIT(SAI_XCR1_CKSTR_SHIFT)
#define SAI_XCR1_SYNCEN_SHIFT 10
#define SAI_XCR1_SYNCEN_MASK GENMASK(11, SAI_XCR1_SYNCEN_SHIFT)
#define SAI_XCR1_SYNCEN_SET(x) ((x) << SAI_XCR1_SYNCEN_SHIFT)
#define SAI_XCR1_MONO_SHIFT 12
#define SAI_XCR1_MONO BIT(SAI_XCR1_MONO_SHIFT)
#define SAI_XCR1_OUTDRIV_SHIFT 13
#define SAI_XCR1_OUTDRIV BIT(SAI_XCR1_OUTDRIV_SHIFT)
#define SAI_XCR1_SAIEN_SHIFT 16
#define SAI_XCR1_SAIEN BIT(SAI_XCR1_SAIEN_SHIFT)
#define SAI_XCR1_DMAEN_SHIFT 17
#define SAI_XCR1_DMAEN BIT(SAI_XCR1_DMAEN_SHIFT)
#define SAI_XCR1_NODIV_SHIFT 19
#define SAI_XCR1_NODIV BIT(SAI_XCR1_NODIV_SHIFT)
#define SAI_XCR1_MCKDIV_SHIFT 20
#define SAI_XCR1_MCKDIV_WIDTH(x) (((x) == STM_SAI_STM32F4) ? 4 : 6)
#define SAI_XCR1_MCKDIV_MASK(x) GENMASK((SAI_XCR1_MCKDIV_SHIFT + (x) - 1),\
SAI_XCR1_MCKDIV_SHIFT)
#define SAI_XCR1_MCKDIV_SET(x) ((x) << SAI_XCR1_MCKDIV_SHIFT)
#define SAI_XCR1_MCKDIV_MAX(x) ((1 << SAI_XCR1_MCKDIV_WIDTH(x)) - 1)
#define SAI_XCR1_OSR_SHIFT 26
#define SAI_XCR1_OSR BIT(SAI_XCR1_OSR_SHIFT)
#define SAI_XCR1_MCKEN_SHIFT 27
#define SAI_XCR1_MCKEN BIT(SAI_XCR1_MCKEN_SHIFT)
/******************* Bit definition for SAI_XCR2 register *******************/
#define SAI_XCR2_FTH_SHIFT 0
#define SAI_XCR2_FTH_MASK GENMASK(2, SAI_XCR2_FTH_SHIFT)
#define SAI_XCR2_FTH_SET(x) ((x) << SAI_XCR2_FTH_SHIFT)
#define SAI_XCR2_FFLUSH_SHIFT 3
#define SAI_XCR2_FFLUSH BIT(SAI_XCR2_FFLUSH_SHIFT)
#define SAI_XCR2_TRIS_SHIFT 4
#define SAI_XCR2_TRIS BIT(SAI_XCR2_TRIS_SHIFT)
#define SAI_XCR2_MUTE_SHIFT 5
#define SAI_XCR2_MUTE BIT(SAI_XCR2_MUTE_SHIFT)
#define SAI_XCR2_MUTEVAL_SHIFT 6
#define SAI_XCR2_MUTEVAL BIT(SAI_XCR2_MUTEVAL_SHIFT)
#define SAI_XCR2_MUTECNT_SHIFT 7
#define SAI_XCR2_MUTECNT_MASK GENMASK(12, SAI_XCR2_MUTECNT_SHIFT)
#define SAI_XCR2_MUTECNT_SET(x) ((x) << SAI_XCR2_MUTECNT_SHIFT)
#define SAI_XCR2_CPL_SHIFT 13
#define SAI_XCR2_CPL BIT(SAI_XCR2_CPL_SHIFT)
#define SAI_XCR2_COMP_SHIFT 14
#define SAI_XCR2_COMP_MASK GENMASK(15, SAI_XCR2_COMP_SHIFT)
#define SAI_XCR2_COMP_SET(x) ((x) << SAI_XCR2_COMP_SHIFT)
/****************** Bit definition for SAI_XFRCR register *******************/
#define SAI_XFRCR_FRL_SHIFT 0
#define SAI_XFRCR_FRL_MASK GENMASK(7, SAI_XFRCR_FRL_SHIFT)
#define SAI_XFRCR_FRL_SET(x) ((x) << SAI_XFRCR_FRL_SHIFT)
#define SAI_XFRCR_FSALL_SHIFT 8
#define SAI_XFRCR_FSALL_MASK GENMASK(14, SAI_XFRCR_FSALL_SHIFT)
#define SAI_XFRCR_FSALL_SET(x) ((x) << SAI_XFRCR_FSALL_SHIFT)
#define SAI_XFRCR_FSDEF_SHIFT 16
#define SAI_XFRCR_FSDEF BIT(SAI_XFRCR_FSDEF_SHIFT)
#define SAI_XFRCR_FSPOL_SHIFT 17
#define SAI_XFRCR_FSPOL BIT(SAI_XFRCR_FSPOL_SHIFT)
#define SAI_XFRCR_FSOFF_SHIFT 18
#define SAI_XFRCR_FSOFF BIT(SAI_XFRCR_FSOFF_SHIFT)
/****************** Bit definition for SAI_XSLOTR register ******************/
#define SAI_XSLOTR_FBOFF_SHIFT 0
#define SAI_XSLOTR_FBOFF_MASK GENMASK(4, SAI_XSLOTR_FBOFF_SHIFT)
#define SAI_XSLOTR_FBOFF_SET(x) ((x) << SAI_XSLOTR_FBOFF_SHIFT)
#define SAI_XSLOTR_SLOTSZ_SHIFT 6
#define SAI_XSLOTR_SLOTSZ_MASK GENMASK(7, SAI_XSLOTR_SLOTSZ_SHIFT)
#define SAI_XSLOTR_SLOTSZ_SET(x) ((x) << SAI_XSLOTR_SLOTSZ_SHIFT)
#define SAI_XSLOTR_NBSLOT_SHIFT 8
#define SAI_XSLOTR_NBSLOT_MASK GENMASK(11, SAI_XSLOTR_NBSLOT_SHIFT)
#define SAI_XSLOTR_NBSLOT_SET(x) ((x) << SAI_XSLOTR_NBSLOT_SHIFT)
#define SAI_XSLOTR_SLOTEN_SHIFT 16
#define SAI_XSLOTR_SLOTEN_WIDTH 16
#define SAI_XSLOTR_SLOTEN_MASK GENMASK(31, SAI_XSLOTR_SLOTEN_SHIFT)
#define SAI_XSLOTR_SLOTEN_SET(x) ((x) << SAI_XSLOTR_SLOTEN_SHIFT)
/******************* Bit definition for SAI_XIMR register *******************/
#define SAI_XIMR_OVRUDRIE BIT(0)
#define SAI_XIMR_MUTEDETIE BIT(1)
#define SAI_XIMR_WCKCFGIE BIT(2)
#define SAI_XIMR_FREQIE BIT(3)
#define SAI_XIMR_CNRDYIE BIT(4)
#define SAI_XIMR_AFSDETIE BIT(5)
#define SAI_XIMR_LFSDETIE BIT(6)
#define SAI_XIMR_SHIFT 0
#define SAI_XIMR_MASK GENMASK(6, SAI_XIMR_SHIFT)
/******************** Bit definition for SAI_XSR register *******************/
#define SAI_XSR_OVRUDR BIT(0)
#define SAI_XSR_MUTEDET BIT(1)
#define SAI_XSR_WCKCFG BIT(2)
#define SAI_XSR_FREQ BIT(3)
#define SAI_XSR_CNRDY BIT(4)
#define SAI_XSR_AFSDET BIT(5)
#define SAI_XSR_LFSDET BIT(6)
#define SAI_XSR_SHIFT 0
#define SAI_XSR_MASK GENMASK(6, SAI_XSR_SHIFT)
/****************** Bit definition for SAI_XCLRFR register ******************/
#define SAI_XCLRFR_COVRUDR BIT(0)
#define SAI_XCLRFR_CMUTEDET BIT(1)
#define SAI_XCLRFR_CWCKCFG BIT(2)
#define SAI_XCLRFR_CFREQ BIT(3)
#define SAI_XCLRFR_CCNRDY BIT(4)
#define SAI_XCLRFR_CAFSDET BIT(5)
#define SAI_XCLRFR_CLFSDET BIT(6)
#define SAI_XCLRFR_SHIFT 0
#define SAI_XCLRFR_MASK GENMASK(6, SAI_XCLRFR_SHIFT)
/****************** Bit definition for SAI_PDMCR register ******************/
#define SAI_PDMCR_PDMEN BIT(0)
#define SAI_PDMCR_MICNBR_SHIFT 4
#define SAI_PDMCR_MICNBR_MASK GENMASK(5, SAI_PDMCR_MICNBR_SHIFT)
#define SAI_PDMCR_MICNBR_SET(x) ((x) << SAI_PDMCR_MICNBR_SHIFT)
#define SAI_PDMCR_CKEN1 BIT(8)
#define SAI_PDMCR_CKEN2 BIT(9)
#define SAI_PDMCR_CKEN3 BIT(10)
#define SAI_PDMCR_CKEN4 BIT(11)
/****************** Bit definition for (SAI_PDMDLY register ****************/
#define SAI_PDMDLY_1L_SHIFT 0
#define SAI_PDMDLY_1L_MASK GENMASK(2, SAI_PDMDLY_1L_SHIFT)
#define SAI_PDMDLY_1L_WIDTH 3
#define SAI_PDMDLY_1R_SHIFT 4
#define SAI_PDMDLY_1R_MASK GENMASK(6, SAI_PDMDLY_1R_SHIFT)
#define SAI_PDMDLY_1R_WIDTH 3
#define SAI_PDMDLY_2L_SHIFT 8
#define SAI_PDMDLY_2L_MASK GENMASK(10, SAI_PDMDLY_2L_SHIFT)
#define SAI_PDMDLY_2L_WIDTH 3
#define SAI_PDMDLY_2R_SHIFT 12
#define SAI_PDMDLY_2R_MASK GENMASK(14, SAI_PDMDLY_2R_SHIFT)
#define SAI_PDMDLY_2R_WIDTH 3
#define SAI_PDMDLY_3L_SHIFT 16
#define SAI_PDMDLY_3L_MASK GENMASK(18, SAI_PDMDLY_3L_SHIFT)
#define SAI_PDMDLY_3L_WIDTH 3
#define SAI_PDMDLY_3R_SHIFT 20
#define SAI_PDMDLY_3R_MASK GENMASK(22, SAI_PDMDLY_3R_SHIFT)
#define SAI_PDMDLY_3R_WIDTH 3
#define SAI_PDMDLY_4L_SHIFT 24
#define SAI_PDMDLY_4L_MASK GENMASK(26, SAI_PDMDLY_4L_SHIFT)
#define SAI_PDMDLY_4L_WIDTH 3
#define SAI_PDMDLY_4R_SHIFT 28
#define SAI_PDMDLY_4R_MASK GENMASK(30, SAI_PDMDLY_4R_SHIFT)
#define SAI_PDMDLY_4R_WIDTH 3
/* Registers below apply to SAI version 2.1 and more */
/* Bit definition for SAI_HWCFGR register */
#define SAI_HWCFGR_FIFO_SIZE GENMASK(7, 0)
#define SAI_HWCFGR_SPDIF_PDM GENMASK(11, 8)
#define SAI_HWCFGR_REGOUT GENMASK(19, 12)
/* Bit definition for SAI_VERR register */
#define SAI_VERR_MIN_MASK GENMASK(3, 0)
#define SAI_VERR_MAJ_MASK GENMASK(7, 4)
/* Bit definition for SAI_IDR register */
#define SAI_IDR_ID_MASK GENMASK(31, 0)
/* Bit definition for SAI_SIDR register */
#define SAI_SIDR_ID_MASK GENMASK(31, 0)
#define SAI_IPIDR_NUMBER 0x00130031
/* SAI version numbers are 1.x for F4. Major version number set to 1 for F4 */
#define STM_SAI_STM32F4 BIT(4)
/* Dummy version number for H7 socs and next */
#define STM_SAI_STM32H7 0x0
#define STM_SAI_IS_F4(ip) ((ip)->conf.version == STM_SAI_STM32F4)
#define STM_SAI_HAS_SPDIF_PDM(ip)\
((ip)->pdata->conf.has_spdif_pdm)
enum stm32_sai_syncout {
STM_SAI_SYNC_OUT_NONE,
STM_SAI_SYNC_OUT_A,
STM_SAI_SYNC_OUT_B,
};
struct stm32_sai_data;
/**
* struct stm32_sai_conf - SAI configuration
* @get_sai_ck_parent: get parent clock of SAI kernel clock
* @version: SAI version
* @fifo_size: SAI fifo size as words number
* @has_spdif_pdm: SAI S/PDIF and PDM features support flag
* @no_dma_burst: Support only DMA single transfers if set
*/
struct stm32_sai_conf {
int (*get_sai_ck_parent)(struct stm32_sai_data *sai);
u32 version;
u32 fifo_size;
bool has_spdif_pdm;
bool no_dma_burst;
};
/**
* struct stm32_sai_data - private data of SAI instance driver
* @pdev: device data pointer
* @base: common register bank virtual base address
* @pclk: SAI bus clock
* @clk_x8k: SAI parent clock for sampling frequencies multiple of 8kHz
* @clk_x11k: SAI parent clock for sampling frequencies multiple of 11kHz
* @conf: SAI hardware capabitilites
* @irq: SAI interrupt line
* @set_sync: pointer to synchro mode configuration callback
* @gcr: SAI Global Configuration Register
*/
struct stm32_sai_data {
struct platform_device *pdev;
void __iomem *base;
struct clk *pclk;
struct clk *clk_x8k;
struct clk *clk_x11k;
struct stm32_sai_conf conf;
int irq;
int (*set_sync)(struct stm32_sai_data *sai,
struct device_node *np_provider, int synco, int synci);
u32 gcr;
};
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff