Commit 76987bfb authored by Mark Brown's avatar Mark Brown

Merge remote-tracking branches 'asoc/topic/simple', 'asoc/topic/spdif',...

Merge remote-tracking branches 'asoc/topic/simple', 'asoc/topic/spdif', 'asoc/topic/st-dfsdm', 'asoc/topic/stm32' and 'asoc/topic/sun4i-i2s' into asoc-next
...@@ -140,6 +140,7 @@ sound { ...@@ -140,6 +140,7 @@ sound {
simple-audio-card,name = "Cubox Audio"; simple-audio-card,name = "Cubox Audio";
simple-audio-card,dai-link@0 { /* I2S - HDMI */ simple-audio-card,dai-link@0 { /* I2S - HDMI */
reg = <0>;
format = "i2s"; format = "i2s";
cpu { cpu {
sound-dai = <&audio1 0>; sound-dai = <&audio1 0>;
...@@ -150,6 +151,7 @@ sound { ...@@ -150,6 +151,7 @@ sound {
}; };
simple-audio-card,dai-link@1 { /* S/PDIF - HDMI */ simple-audio-card,dai-link@1 { /* S/PDIF - HDMI */
reg = <1>;
cpu { cpu {
sound-dai = <&audio1 1>; sound-dai = <&audio1 1>;
}; };
...@@ -159,6 +161,7 @@ sound { ...@@ -159,6 +161,7 @@ sound {
}; };
simple-audio-card,dai-link@2 { /* S/PDIF - S/PDIF */ simple-audio-card,dai-link@2 { /* S/PDIF - S/PDIF */
reg = <2>;
cpu { cpu {
sound-dai = <&audio1 1>; sound-dai = <&audio1 1>;
}; };
......
STMicroelectronics Audio Digital Filter Sigma Delta modulators(DFSDM)
The DFSDM allows PDM microphones capture through SPI interface. The Audio
interface is seems as a sub block of the DFSDM device.
For details on DFSDM bindings refer to ../iio/adc/st,stm32-dfsdm-adc.txt
Required properties:
- compatible: "st,stm32h7-dfsdm-dai".
- #sound-dai-cells : Must be equal to 0
- io-channels : phandle to iio dfsdm instance node.
Example of a sound card using audio DFSDM node.
sound_card {
compatible = "audio-graph-card";
dais = <&cpu_port>;
};
dfsdm: dfsdm@40017000 {
compatible = "st,stm32h7-dfsdm";
reg = <0x40017000 0x400>;
clocks = <&rcc DFSDM1_CK>;
clock-names = "dfsdm";
#interrupt-cells = <1>;
#address-cells = <1>;
#size-cells = <0>;
dfsdm_adc0: filter@0 {
compatible = "st,stm32-dfsdm-dmic";
reg = <0>;
interrupts = <110>;
dmas = <&dmamux1 101 0x400 0x00>;
dma-names = "rx";
st,adc-channels = <1>;
st,adc-channel-names = "dmic0";
st,adc-channel-types = "SPI_R";
st,adc-channel-clk-src = "CLKOUT";
st,filter-order = <5>;
dfsdm_dai0: dfsdm-dai {
compatible = "st,stm32h7-dfsdm-dai";
#sound-dai-cells = <0>;
io-channels = <&dfsdm_adc0 0>;
cpu_port: port {
dfsdm_endpoint: endpoint {
remote-endpoint = <&dmic0_endpoint>;
};
};
};
};
dmic0: dmic@0 {
compatible = "dmic-codec";
#sound-dai-cells = <0>;
port {
dmic0_endpoint: endpoint {
remote-endpoint = <&dfsdm_endpoint>;
};
};
};
...@@ -20,11 +20,6 @@ Required properties: ...@@ -20,11 +20,6 @@ Required properties:
Optional properties: Optional properties:
- resets: Reference to a reset controller asserting the SAI - resets: Reference to a reset controller asserting the SAI
- st,sync: specify synchronization mode.
By default SAI sub-block is in asynchronous mode.
This property sets SAI sub-block as slave of another SAI sub-block.
Must contain the phandle and index of the sai sub-block providing
the synchronization.
SAI subnodes: SAI subnodes:
Two subnodes corresponding to SAI sub-block instances A et B can be defined. Two subnodes corresponding to SAI sub-block instances A et B can be defined.
...@@ -44,6 +39,13 @@ SAI subnodes required properties: ...@@ -44,6 +39,13 @@ SAI subnodes required properties:
- pinctrl-names: should contain only value "default" - pinctrl-names: should contain only value "default"
- pinctrl-0: see Documentation/devicetree/bindings/pinctrl/pinctrl-stm32.txt - pinctrl-0: see Documentation/devicetree/bindings/pinctrl/pinctrl-stm32.txt
SAI subnodes Optional properties:
- st,sync: specify synchronization mode.
By default SAI sub-block is in asynchronous mode.
This property sets SAI sub-block as slave of another SAI sub-block.
Must contain the phandle and index of the sai sub-block providing
the synchronization.
The device node should contain one 'port' child node with one child 'endpoint' The device node should contain one 'port' child node with one child 'endpoint'
node, according to the bindings defined in Documentation/devicetree/bindings/ node, according to the bindings defined in Documentation/devicetree/bindings/
graph.txt. graph.txt.
......
...@@ -35,9 +35,10 @@ static const struct snd_soc_dapm_route dir_routes[] = { ...@@ -35,9 +35,10 @@ static const struct snd_soc_dapm_route dir_routes[] = {
#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ #define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S20_3LE | \ SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE | \
SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
static const struct snd_soc_codec_driver soc_codec_spdif_dir = { static struct snd_soc_codec_driver soc_codec_spdif_dir = {
.component_driver = { .component_driver = {
.dapm_widgets = dir_widgets, .dapm_widgets = dir_widgets,
.num_dapm_widgets = ARRAY_SIZE(dir_widgets), .num_dapm_widgets = ARRAY_SIZE(dir_widgets),
......
...@@ -27,7 +27,8 @@ ...@@ -27,7 +27,8 @@
#define STUB_RATES SNDRV_PCM_RATE_8000_192000 #define STUB_RATES SNDRV_PCM_RATE_8000_192000
#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ #define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S20_3LE | \ SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE) SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
static const struct snd_soc_dapm_widget dit_widgets[] = { static const struct snd_soc_dapm_widget dit_widgets[] = {
SND_SOC_DAPM_OUTPUT("spdif-out"), SND_SOC_DAPM_OUTPUT("spdif-out"),
...@@ -37,7 +38,7 @@ static const struct snd_soc_dapm_route dit_routes[] = { ...@@ -37,7 +38,7 @@ static const struct snd_soc_dapm_route dit_routes[] = {
{ "spdif-out", NULL, "Playback" }, { "spdif-out", NULL, "Playback" },
}; };
static const struct snd_soc_codec_driver soc_codec_spdif_dit = { static struct snd_soc_codec_driver soc_codec_spdif_dit = {
.component_driver = { .component_driver = {
.dapm_widgets = dit_widgets, .dapm_widgets = dit_widgets,
.num_dapm_widgets = ARRAY_SIZE(dit_widgets), .num_dapm_widgets = ARRAY_SIZE(dit_widgets),
......
...@@ -28,4 +28,16 @@ config SND_SOC_STM32_SPDIFRX ...@@ -28,4 +28,16 @@ config SND_SOC_STM32_SPDIFRX
help help
Say Y if you want to enable S/PDIF capture for STM32 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 endmenu
...@@ -13,3 +13,6 @@ obj-$(CONFIG_SND_SOC_STM32_I2S) += snd-soc-stm32-i2s.o ...@@ -13,3 +13,6 @@ obj-$(CONFIG_SND_SOC_STM32_I2S) += snd-soc-stm32-i2s.o
# SPDIFRX # SPDIFRX
snd-soc-stm32-spdifrx-objs := stm32_spdifrx.o snd-soc-stm32-spdifrx-objs := stm32_spdifrx.o
obj-$(CONFIG_SND_SOC_STM32_SPDIFRX) += snd-soc-stm32-spdifrx.o obj-$(CONFIG_SND_SOC_STM32_SPDIFRX) += snd-soc-stm32-spdifrx.o
#DFSDM
obj-$(CONFIG_SND_SOC_STM32_DFSDM) += stm32_adfsdm.o
// 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/platform_device.h>
#include <linux/slab.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;
};
static const struct snd_pcm_hardware stm32_adfsdm_pcm_hw = {
.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_PAUSE,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rate_min = 8000,
.rate_max = 32000,
.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);
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;
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), "%d\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_S32_LE,
.rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
SNDRV_PCM_RATE_32000),
},
.ops = &stm32_adfsdm_dai_ops,
};
static const struct snd_soc_component_driver stm32_adfsdm_dai_component = {
.name = "stm32_dfsdm_audio",
};
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 = priv->substream->private_data;
u8 *pcm_buff = priv->pcm_buff;
u8 *src_buff = (u8 *)data;
unsigned int buff_size = snd_pcm_lib_buffer_bytes(priv->substream);
unsigned int period_size = snd_pcm_lib_period_bytes(priv->substream);
unsigned int old_pos = priv->pos;
unsigned int cur_size = size;
dev_dbg(rtd->dev, "%s: buff_add :%p, pos = %d, size = %zu\n",
__func__, &pcm_buff[priv->pos], priv->pos, size);
if ((priv->pos + size) > buff_size) {
memcpy(&pcm_buff[priv->pos], src_buff, buff_size - priv->pos);
cur_size -= buff_size - priv->pos;
priv->pos = 0;
}
memcpy(&pcm_buff[priv->pos], &src_buff[size - cur_size], cur_size);
priv->pos = (priv->pos + cur_size) % buff_size;
if (cur_size != size || (old_pos && (old_pos % period_size < size)))
snd_pcm_period_elapsed(priv->substream);
return 0;
}
static int stm32_adfsdm_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct stm32_adfsdm_priv *priv =
snd_soc_dai_get_drvdata(rtd->cpu_dai);
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_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
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_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct stm32_adfsdm_priv *priv =
snd_soc_dai_get_drvdata(rtd->cpu_dai);
snd_pcm_lib_free_pages(substream);
priv->substream = NULL;
return 0;
}
static snd_pcm_uframes_t stm32_adfsdm_pcm_pointer(
struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct stm32_adfsdm_priv *priv =
snd_soc_dai_get_drvdata(rtd->cpu_dai);
return bytes_to_frames(substream->runtime, priv->pos);
}
static int stm32_adfsdm_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct stm32_adfsdm_priv *priv =
snd_soc_dai_get_drvdata(rtd->cpu_dai);
int ret;
ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
if (ret < 0)
return ret;
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_hw_free(struct snd_pcm_substream *substream)
{
snd_pcm_lib_free_pages(substream);
return 0;
}
static struct snd_pcm_ops stm32_adfsdm_pcm_ops = {
.open = stm32_adfsdm_pcm_open,
.close = stm32_adfsdm_pcm_close,
.hw_params = stm32_adfsdm_pcm_hw_params,
.hw_free = stm32_adfsdm_pcm_hw_free,
.trigger = stm32_adfsdm_trigger,
.pointer = stm32_adfsdm_pcm_pointer,
};
static int stm32_adfsdm_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_pcm *pcm = rtd->pcm;
struct stm32_adfsdm_priv *priv =
snd_soc_dai_get_drvdata(rtd->cpu_dai);
unsigned int size = DFSDM_MAX_PERIODS * DFSDM_MAX_PERIOD_SIZE;
return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
priv->dev, size, size);
}
static void stm32_adfsdm_pcm_free(struct snd_pcm *pcm)
{
struct snd_pcm_substream *substream;
struct snd_soc_pcm_runtime *rtd;
struct stm32_adfsdm_priv *priv;
substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
if (substream) {
rtd = substream->private_data;
priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
snd_pcm_lib_preallocate_free_for_all(pcm);
}
}
static struct snd_soc_platform_driver stm32_adfsdm_soc_platform = {
.ops = &stm32_adfsdm_pcm_ops,
.pcm_new = stm32_adfsdm_pcm_new,
.pcm_free = stm32_adfsdm_pcm_free,
};
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;
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, NULL, NULL);
if (IS_ERR(priv->iio_cb))
return PTR_ERR(priv->iio_cb);
ret = devm_snd_soc_register_platform(&pdev->dev,
&stm32_adfsdm_soc_platform);
if (ret < 0)
dev_err(&pdev->dev, "%s: Failed to register PCM platform\n",
__func__);
return ret;
}
static struct platform_driver stm32_adfsdm_driver = {
.driver = {
.name = STM32_ADFSDM_DRV_NAME,
.of_match_table = stm32_adfsdm_of_match,
},
.probe = stm32_adfsdm_probe,
};
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);
...@@ -28,16 +28,6 @@ ...@@ -28,16 +28,6 @@
#include "stm32_sai.h" #include "stm32_sai.h"
static LIST_HEAD(sync_providers);
static DEFINE_MUTEX(sync_mutex);
struct sync_provider {
struct list_head link;
struct device_node *node;
int (*sync_conf)(void *data, int synco);
void *data;
};
static const struct stm32_sai_conf stm32_sai_conf_f4 = { static const struct stm32_sai_conf stm32_sai_conf_f4 = {
.version = SAI_STM32F4, .version = SAI_STM32F4,
}; };
...@@ -70,9 +60,8 @@ static int stm32_sai_sync_conf_client(struct stm32_sai_data *sai, int synci) ...@@ -70,9 +60,8 @@ static int stm32_sai_sync_conf_client(struct stm32_sai_data *sai, int synci)
return 0; return 0;
} }
static int stm32_sai_sync_conf_provider(void *data, int synco) static int stm32_sai_sync_conf_provider(struct stm32_sai_data *sai, int synco)
{ {
struct stm32_sai_data *sai = (struct stm32_sai_data *)data;
u32 prev_synco; u32 prev_synco;
int ret; int ret;
...@@ -103,83 +92,42 @@ static int stm32_sai_sync_conf_provider(void *data, int synco) ...@@ -103,83 +92,42 @@ static int stm32_sai_sync_conf_provider(void *data, int synco)
return 0; return 0;
} }
static int stm32_sai_set_sync_provider(struct device_node *np, int synco) static int stm32_sai_set_sync(struct stm32_sai_data *sai_client,
{
struct sync_provider *provider;
int ret;
mutex_lock(&sync_mutex);
list_for_each_entry(provider, &sync_providers, link) {
if (provider->node == np) {
ret = provider->sync_conf(provider->data, synco);
mutex_unlock(&sync_mutex);
return ret;
}
}
mutex_unlock(&sync_mutex);
/* SAI sync provider not found */
return -ENODEV;
}
static int stm32_sai_set_sync(struct stm32_sai_data *sai,
struct device_node *np_provider, struct device_node *np_provider,
int synco, int synci) int synco, int synci)
{ {
struct platform_device *pdev = of_find_device_by_node(np_provider);
struct stm32_sai_data *sai_provider;
int ret; int ret;
/* Configure sync client */ if (!pdev) {
stm32_sai_sync_conf_client(sai, synci); dev_err(&sai_client->pdev->dev,
"Device not found for node %s\n", np_provider->name);
return -ENODEV;
}
/* Configure sync provider */ sai_provider = platform_get_drvdata(pdev);
ret = stm32_sai_set_sync_provider(np_provider, synco); 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; return ret;
}
static int stm32_sai_sync_add_provider(struct platform_device *pdev,
void *data)
{
struct sync_provider *sp;
sp = devm_kzalloc(&pdev->dev, sizeof(*sp), GFP_KERNEL);
if (!sp)
return -ENOMEM;
sp->node = of_node_get(pdev->dev.of_node); /* Configure sync provider */
sp->data = data; return stm32_sai_sync_conf_provider(sai_provider, synco);
sp->sync_conf = &stm32_sai_sync_conf_provider;
mutex_lock(&sync_mutex);
list_add(&sp->link, &sync_providers);
mutex_unlock(&sync_mutex);
return 0;
}
static void stm32_sai_sync_del_provider(struct device_node *np)
{
struct sync_provider *sp;
mutex_lock(&sync_mutex);
list_for_each_entry(sp, &sync_providers, link) {
if (sp->node == np) {
list_del(&sp->link);
of_node_put(sp->node);
break;
}
}
mutex_unlock(&sync_mutex);
} }
static int stm32_sai_probe(struct platform_device *pdev) static int stm32_sai_probe(struct platform_device *pdev)
{ {
struct device_node *np = pdev->dev.of_node;
struct stm32_sai_data *sai; struct stm32_sai_data *sai;
struct reset_control *rst; struct reset_control *rst;
struct resource *res; struct resource *res;
const struct of_device_id *of_id; const struct of_device_id *of_id;
int ret;
sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
if (!sai) if (!sai)
...@@ -231,28 +179,11 @@ static int stm32_sai_probe(struct platform_device *pdev) ...@@ -231,28 +179,11 @@ static int stm32_sai_probe(struct platform_device *pdev)
reset_control_deassert(rst); reset_control_deassert(rst);
} }
ret = stm32_sai_sync_add_provider(pdev, sai);
if (ret < 0)
return ret;
sai->set_sync = &stm32_sai_set_sync;
sai->pdev = pdev; sai->pdev = pdev;
sai->set_sync = &stm32_sai_set_sync;
platform_set_drvdata(pdev, sai); platform_set_drvdata(pdev, sai);
ret = of_platform_populate(np, NULL, NULL, &pdev->dev); return devm_of_platform_populate(&pdev->dev);
if (ret < 0)
stm32_sai_sync_del_provider(np);
return ret;
}
static int stm32_sai_remove(struct platform_device *pdev)
{
of_platform_depopulate(&pdev->dev);
stm32_sai_sync_del_provider(pdev->dev.of_node);
return 0;
} }
MODULE_DEVICE_TABLE(of, stm32_sai_ids); MODULE_DEVICE_TABLE(of, stm32_sai_ids);
...@@ -263,7 +194,6 @@ static struct platform_driver stm32_sai_driver = { ...@@ -263,7 +194,6 @@ static struct platform_driver stm32_sai_driver = {
.of_match_table = stm32_sai_ids, .of_match_table = stm32_sai_ids,
}, },
.probe = stm32_sai_probe, .probe = stm32_sai_probe,
.remove = stm32_sai_remove,
}; };
module_platform_driver(stm32_sai_driver); module_platform_driver(stm32_sai_driver);
......
...@@ -269,10 +269,11 @@ static bool sun4i_i2s_oversample_is_valid(unsigned int oversample) ...@@ -269,10 +269,11 @@ static bool sun4i_i2s_oversample_is_valid(unsigned int oversample)
return false; return false;
} }
static int sun4i_i2s_set_clk_rate(struct sun4i_i2s *i2s, static int sun4i_i2s_set_clk_rate(struct snd_soc_dai *dai,
unsigned int rate, unsigned int rate,
unsigned int word_size) unsigned int word_size)
{ {
struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
unsigned int oversample_rate, clk_rate; unsigned int oversample_rate, clk_rate;
int bclk_div, mclk_div; int bclk_div, mclk_div;
int ret; int ret;
...@@ -300,6 +301,7 @@ static int sun4i_i2s_set_clk_rate(struct sun4i_i2s *i2s, ...@@ -300,6 +301,7 @@ static int sun4i_i2s_set_clk_rate(struct sun4i_i2s *i2s,
break; break;
default: default:
dev_err(dai->dev, "Unsupported sample rate: %u\n", rate);
return -EINVAL; return -EINVAL;
} }
...@@ -308,18 +310,25 @@ static int sun4i_i2s_set_clk_rate(struct sun4i_i2s *i2s, ...@@ -308,18 +310,25 @@ static int sun4i_i2s_set_clk_rate(struct sun4i_i2s *i2s,
return ret; return ret;
oversample_rate = i2s->mclk_freq / rate; oversample_rate = i2s->mclk_freq / rate;
if (!sun4i_i2s_oversample_is_valid(oversample_rate)) if (!sun4i_i2s_oversample_is_valid(oversample_rate)) {
dev_err(dai->dev, "Unsupported oversample rate: %d\n",
oversample_rate);
return -EINVAL; return -EINVAL;
}
bclk_div = sun4i_i2s_get_bclk_div(i2s, oversample_rate, bclk_div = sun4i_i2s_get_bclk_div(i2s, oversample_rate,
word_size); word_size);
if (bclk_div < 0) if (bclk_div < 0) {
dev_err(dai->dev, "Unsupported BCLK divider: %d\n", bclk_div);
return -EINVAL; return -EINVAL;
}
mclk_div = sun4i_i2s_get_mclk_div(i2s, oversample_rate, mclk_div = sun4i_i2s_get_mclk_div(i2s, oversample_rate,
clk_rate, rate); clk_rate, rate);
if (mclk_div < 0) if (mclk_div < 0) {
dev_err(dai->dev, "Unsupported MCLK divider: %d\n", mclk_div);
return -EINVAL; return -EINVAL;
}
/* Adjust the clock division values if needed */ /* Adjust the clock division values if needed */
bclk_div += i2s->variant->bclk_offset; bclk_div += i2s->variant->bclk_offset;
...@@ -349,8 +358,11 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, ...@@ -349,8 +358,11 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
u32 width; u32 width;
channels = params_channels(params); channels = params_channels(params);
if (channels != 2) if (channels != 2) {
dev_err(dai->dev, "Unsupported number of channels: %d\n",
channels);
return -EINVAL; return -EINVAL;
}
if (i2s->variant->has_chcfg) { if (i2s->variant->has_chcfg) {
regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG, regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG,
...@@ -382,6 +394,8 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, ...@@ -382,6 +394,8 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
width = DMA_SLAVE_BUSWIDTH_2_BYTES; width = DMA_SLAVE_BUSWIDTH_2_BYTES;
break; break;
default: default:
dev_err(dai->dev, "Unsupported physical sample width: %d\n",
params_physical_width(params));
return -EINVAL; return -EINVAL;
} }
i2s->playback_dma_data.addr_width = width; i2s->playback_dma_data.addr_width = width;
...@@ -393,6 +407,8 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, ...@@ -393,6 +407,8 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
break; break;
default: default:
dev_err(dai->dev, "Unsupported sample width: %d\n",
params_width(params));
return -EINVAL; return -EINVAL;
} }
...@@ -401,7 +417,7 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, ...@@ -401,7 +417,7 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
regmap_field_write(i2s->field_fmt_sr, regmap_field_write(i2s->field_fmt_sr,
sr + i2s->variant->fmt_offset); sr + i2s->variant->fmt_offset);
return sun4i_i2s_set_clk_rate(i2s, params_rate(params), return sun4i_i2s_set_clk_rate(dai, params_rate(params),
params_width(params)); params_width(params));
} }
...@@ -426,6 +442,8 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) ...@@ -426,6 +442,8 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
val = SUN4I_I2S_FMT0_FMT_RIGHT_J; val = SUN4I_I2S_FMT0_FMT_RIGHT_J;
break; break;
default: default:
dev_err(dai->dev, "Unsupported format: %d\n",
fmt & SND_SOC_DAIFMT_FORMAT_MASK);
return -EINVAL; return -EINVAL;
} }
...@@ -464,6 +482,8 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) ...@@ -464,6 +482,8 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
case SND_SOC_DAIFMT_NB_NF: case SND_SOC_DAIFMT_NB_NF:
break; break;
default: default:
dev_err(dai->dev, "Unsupported clock polarity: %d\n",
fmt & SND_SOC_DAIFMT_INV_MASK);
return -EINVAL; return -EINVAL;
} }
...@@ -482,6 +502,8 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) ...@@ -482,6 +502,8 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
val = SUN4I_I2S_CTRL_MODE_SLAVE; val = SUN4I_I2S_CTRL_MODE_SLAVE;
break; break;
default: default:
dev_err(dai->dev, "Unsupported slave setting: %d\n",
fmt & SND_SOC_DAIFMT_MASTER_MASK);
return -EINVAL; return -EINVAL;
} }
regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
...@@ -504,6 +526,8 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) ...@@ -504,6 +526,8 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
val = 0; val = 0;
break; break;
default: default:
dev_err(dai->dev, "Unsupported slave setting: %d\n",
fmt & SND_SOC_DAIFMT_MASTER_MASK);
return -EINVAL; return -EINVAL;
} }
regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment