diff options
Diffstat (limited to 'sound/soc/stm')
-rw-r--r-- | sound/soc/stm/stm32_adfsdm.c | 49 | ||||
-rw-r--r-- | sound/soc/stm/stm32_i2s.c | 60 | ||||
-rw-r--r-- | sound/soc/stm/stm32_sai.c | 44 | ||||
-rw-r--r-- | sound/soc/stm/stm32_sai.h | 54 | ||||
-rw-r--r-- | sound/soc/stm/stm32_sai_sub.c | 14 | ||||
-rw-r--r-- | sound/soc/stm/stm32_spdifrx.c | 37 |
6 files changed, 219 insertions, 39 deletions
diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c index cc517e007039..3c9a9deec9af 100644 --- a/sound/soc/stm/stm32_adfsdm.c +++ b/sound/soc/stm/stm32_adfsdm.c @@ -45,7 +45,7 @@ struct stm32_adfsdm_priv { 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_S32_LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, .rate_min = 8000, .rate_max = 32000, @@ -141,7 +141,8 @@ static const struct snd_soc_dai_driver stm32_adfsdm_dai = { .capture = { .channels_min = 1, .channels_max = 1, - .formats = SNDRV_PCM_FMTBIT_S32_LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000), }, @@ -152,30 +153,58 @@ static const struct snd_soc_component_driver stm32_adfsdm_dai_component = { .name = "stm32_dfsdm_audio", }; +static void 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; 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 = 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; + 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 :%pK, pos = %d, size = %zu\n", - __func__, &pcm_buff[priv->pos], priv->pos, size); + __func__, &pcm_buff[priv->pos], priv->pos, src_size); - if ((priv->pos + size) > buff_size) { - memcpy(&pcm_buff[priv->pos], src_buff, buff_size - priv->pos); + if ((priv->pos + src_size) > buff_size) { + if (format == SNDRV_PCM_FORMAT_S16_LE) + 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; } - memcpy(&pcm_buff[priv->pos], &src_buff[size - cur_size], cur_size); + if (format == SNDRV_PCM_FORMAT_S16_LE) + 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 != size || (old_pos && (old_pos % period_size < size))) + if (cur_size != src_size || (old_pos && (old_pos % period_size < size))) snd_pcm_period_elapsed(priv->substream); return 0; diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c index 01ed5e4c1cc0..ba6452dab69b 100644 --- a/sound/soc/stm/stm32_i2s.c +++ b/sound/soc/stm/stm32_i2s.c @@ -6,6 +6,7 @@ * 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> @@ -27,6 +28,10 @@ #define STM32_I2S_TXDR_REG 0X20 #define STM32_I2S_RXDR_REG 0x30 #define STM32_I2S_CGFR_REG 0X50 +#define STM32_I2S_HWCFGR_REG 0x3F0 +#define STM32_I2S_VERR_REG 0x3F4 +#define STM32_I2S_IPIDR_REG 0x3F8 +#define STM32_I2S_SIDR_REG 0x3FC /* Bit definition for SPI2S_CR1 register */ #define I2S_CR1_SPE BIT(0) @@ -133,6 +138,23 @@ #define I2S_CGFR_ODD BIT(I2S_CGFR_ODD_SHIFT) #define I2S_CGFR_MCKOE BIT(25) +/* Registers below apply to I2S version 1.1 and more */ + +/* Bit definition for SPI_HWCFGR register */ +#define I2S_HWCFGR_I2S_SUPPORT_MASK GENMASK(15, 12) + +/* Bit definition for SPI_VERR register */ +#define I2S_VERR_MIN_MASK GENMASK(3, 0) +#define I2S_VERR_MAJ_MASK GENMASK(7, 4) + +/* Bit definition for SPI_IPIDR register */ +#define I2S_IPIDR_ID_MASK GENMASK(31, 0) + +/* Bit definition for SPI_SIDR register */ +#define I2S_SIDR_ID_MASK GENMASK(31, 0) + +#define I2S_IPIDR_NUMBER 0x00130022 + enum i2s_master_mode { I2S_MS_NOT_SET, I2S_MS_MASTER, @@ -270,6 +292,10 @@ static bool stm32_i2s_readable_reg(struct device *dev, unsigned int reg) case STM32_I2S_SR_REG: case STM32_I2S_RXDR_REG: case STM32_I2S_CGFR_REG: + case STM32_I2S_HWCFGR_REG: + case STM32_I2S_VERR_REG: + case STM32_I2S_IPIDR_REG: + case STM32_I2S_SIDR_REG: return true; default: return false; @@ -701,10 +727,11 @@ static const struct regmap_config stm32_h7_i2s_regmap_conf = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, - .max_register = STM32_I2S_CGFR_REG, + .max_register = STM32_I2S_SIDR_REG, .readable_reg = stm32_i2s_readable_reg, .volatile_reg = stm32_i2s_volatile_reg, .writeable_reg = stm32_i2s_writeable_reg, + .num_reg_defaults_raw = STM32_I2S_SIDR_REG / sizeof(u32) + 1, .fast_io = true, .cache_type = REGCACHE_FLAT, }; @@ -855,6 +882,7 @@ static int stm32_i2s_parse_dt(struct platform_device *pdev, static int stm32_i2s_probe(struct platform_device *pdev) { struct stm32_i2s_data *i2s; + u32 val; int ret; i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); @@ -893,8 +921,34 @@ static int stm32_i2s_probe(struct platform_device *pdev) return ret; /* Set SPI/I2S in i2s mode */ - return regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, - I2S_CGFR_I2SMOD, I2S_CGFR_I2SMOD); + ret = regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, + I2S_CGFR_I2SMOD, I2S_CGFR_I2SMOD); + if (ret) + return ret; + + ret = regmap_read(i2s->regmap, STM32_I2S_IPIDR_REG, &val); + if (ret) + return ret; + + if (val == I2S_IPIDR_NUMBER) { + ret = regmap_read(i2s->regmap, STM32_I2S_HWCFGR_REG, &val); + if (ret) + return ret; + + if (!FIELD_GET(I2S_HWCFGR_I2S_SUPPORT_MASK, val)) { + dev_err(&pdev->dev, + "Device does not support i2s mode\n"); + return -EPERM; + } + + ret = regmap_read(i2s->regmap, STM32_I2S_VERR_REG, &val); + + dev_dbg(&pdev->dev, "I2S version: %lu.%lu registered\n", + FIELD_GET(I2S_VERR_MAJ_MASK, val), + FIELD_GET(I2S_VERR_MIN_MASK, val)); + } + + return ret; } MODULE_DEVICE_TABLE(of, stm32_i2s_ids); diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c index de3d25be68d8..63f68e663676 100644 --- a/sound/soc/stm/stm32_sai.c +++ b/sound/soc/stm/stm32_sai.c @@ -20,13 +20,20 @@ #include "stm32_sai.h" static const struct stm32_sai_conf stm32_sai_conf_f4 = { - .version = SAI_STM32F4, - .has_spdif = false, + .version = STM_SAI_STM32F4, + .fifo_size = 8, + .has_spdif_pdm = false, }; +/* + * Default settings for stm32 H7 socs and next. + * These default settings will be overridden if the soc provides + * support of hardware configuration registers. + */ static const struct stm32_sai_conf stm32_sai_conf_h7 = { - .version = SAI_STM32H7, - .has_spdif = true, + .version = STM_SAI_STM32H7, + .fifo_size = 8, + .has_spdif_pdm = true, }; static const struct of_device_id stm32_sai_ids[] = { @@ -147,6 +154,8 @@ static int stm32_sai_probe(struct platform_device *pdev) struct reset_control *rst; struct resource *res; const struct of_device_id *of_id; + u32 val; + int ret; sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); if (!sai) @@ -159,7 +168,8 @@ static int stm32_sai_probe(struct platform_device *pdev) of_id = of_match_device(stm32_sai_ids, &pdev->dev); if (of_id) - sai->conf = (struct stm32_sai_conf *)of_id->data; + memcpy(&sai->conf, (const struct stm32_sai_conf *)of_id->data, + sizeof(struct stm32_sai_conf)); else return -EINVAL; @@ -198,6 +208,30 @@ static int stm32_sai_probe(struct platform_device *pdev) 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->pdev = pdev; sai->set_sync = &stm32_sai_set_sync; platform_set_drvdata(pdev, sai); diff --git a/sound/soc/stm/stm32_sai.h b/sound/soc/stm/stm32_sai.h index f78dfdb5b9be..33e4bff8c2f5 100644 --- a/sound/soc/stm/stm32_sai.h +++ b/sound/soc/stm/stm32_sai.h @@ -27,6 +27,12 @@ #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 @@ -72,7 +78,7 @@ #define SAI_XCR1_NODIV BIT(SAI_XCR1_NODIV_SHIFT) #define SAI_XCR1_MCKDIV_SHIFT 20 -#define SAI_XCR1_MCKDIV_WIDTH(x) (((x) == SAI_STM32F4) ? 4 : 6) +#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) @@ -224,8 +230,33 @@ #define SAI_PDMDLY_4R_MASK GENMASK(30, SAI_PDMDLY_4R_SHIFT) #define SAI_PDMDLY_4R_WIDTH 3 -#define STM_SAI_IS_F4(ip) ((ip)->conf->version == SAI_STM32F4) -#define STM_SAI_IS_H7(ip) ((ip)->conf->version == SAI_STM32H7) +/* 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, @@ -233,19 +264,16 @@ enum stm32_sai_syncout { STM_SAI_SYNC_OUT_B, }; -enum stm32_sai_version { - SAI_STM32F4, - SAI_STM32H7 -}; - /** * struct stm32_sai_conf - SAI configuration * @version: SAI version - * @has_spdif: SAI S/PDIF support flag + * @fifo_size: SAI fifo size as words number + * @has_spdif_pdm: SAI S/PDIF and PDM features support flag */ struct stm32_sai_conf { - int version; - bool has_spdif; + u32 version; + u32 fifo_size; + bool has_spdif_pdm; }; /** @@ -255,7 +283,7 @@ struct stm32_sai_conf { * @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 - * @version: SOC version + * @conf: SAI hardware capabitilites * @irq: SAI interrupt line * @set_sync: pointer to synchro mode configuration callback * @gcr: SAI Global Configuration Register @@ -266,7 +294,7 @@ struct stm32_sai_data { struct clk *pclk; struct clk *clk_x8k; struct clk *clk_x11k; - struct stm32_sai_conf *conf; + struct stm32_sai_conf conf; int irq; int (*set_sync)(struct stm32_sai_data *sai, struct device_node *np_provider, int synco, int synci); diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index 25c9cb67d6dd..d7501f88aaa6 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -35,7 +35,6 @@ #define SAI_DATASIZE_24 0x6 #define SAI_DATASIZE_32 0x7 -#define STM_SAI_FIFO_SIZE 8 #define STM_SAI_DAI_NAME_SIZE 15 #define STM_SAI_IS_PLAYBACK(ip) ((ip)->dir == SNDRV_PCM_STREAM_PLAYBACK) @@ -53,7 +52,8 @@ #define SAI_SYNC_EXTERNAL 0x2 #define STM_SAI_PROTOCOL_IS_SPDIF(ip) ((ip)->spdif) -#define STM_SAI_HAS_SPDIF(x) ((x)->pdata->conf->has_spdif) +#define STM_SAI_HAS_SPDIF(x) ((x)->pdata->conf.has_spdif_pdm) +#define STM_SAI_HAS_PDM(x) ((x)->pdata->conf.has_spdif_pdm) #define STM_SAI_HAS_EXT_SYNC(x) (!STM_SAI_IS_F4(sai->pdata)) #define SAI_IEC60958_BLOCK_FRAMES 192 @@ -264,7 +264,7 @@ static int stm32_sai_get_clk_div(struct stm32_sai_sub_data *sai, unsigned long input_rate, unsigned long output_rate) { - int version = sai->pdata->conf->version; + int version = sai->pdata->conf.version; int div; div = DIV_ROUND_CLOSEST(input_rate, output_rate); @@ -285,7 +285,7 @@ static int stm32_sai_get_clk_div(struct stm32_sai_sub_data *sai, static int stm32_sai_set_clk_div(struct stm32_sai_sub_data *sai, unsigned int div) { - int version = sai->pdata->conf->version; + int version = sai->pdata->conf.version; int ret, cr1, mask; if (div > SAI_XCR1_MCKDIV_MAX(version)) { @@ -1138,6 +1138,8 @@ static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) * constraints). */ sai->dma_params.maxburst = 4; + if (sai->pdata->conf.fifo_size < 8) + sai->dma_params.maxburst = 1; /* Buswidth will be set by framework at runtime */ sai->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED; @@ -1305,8 +1307,8 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, sai->phys_addr = res->start; sai->regmap_config = &stm32_sai_sub_regmap_config_f4; - /* Note: PDM registers not available for H7 sub-block B */ - if (STM_SAI_IS_H7(sai->pdata) && STM_SAI_IS_SUB_A(sai)) + /* Note: PDM registers not available for sub-block B */ + if (STM_SAI_HAS_PDM(sai) && STM_SAI_IS_SUB_A(sai)) sai->regmap_config = &stm32_sai_sub_regmap_config_h7; sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "sai_ck", diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c index 56d79695577c..ee71b898897b 100644 --- a/sound/soc/stm/stm32_spdifrx.c +++ b/sound/soc/stm/stm32_spdifrx.c @@ -6,6 +6,7 @@ * Author(s): Olivier Moysan <olivier.moysan@st.com> for STMicroelectronics. */ +#include <linux/bitfield.h> #include <linux/clk.h> #include <linux/completion.h> #include <linux/delay.h> @@ -26,6 +27,9 @@ #define STM32_SPDIFRX_DR 0x10 #define STM32_SPDIFRX_CSR 0x14 #define STM32_SPDIFRX_DIR 0x18 +#define STM32_SPDIFRX_VERR 0x3F4 +#define STM32_SPDIFRX_IDR 0x3F8 +#define STM32_SPDIFRX_SIDR 0x3FC /* Bit definition for SPDIF_CR register */ #define SPDIFRX_CR_SPDIFEN_SHIFT 0 @@ -159,6 +163,18 @@ #define SPDIFRX_SPDIFEN_SYNC 0x1 #define SPDIFRX_SPDIFEN_ENABLE 0x3 +/* Bit definition for SPDIFRX_VERR register */ +#define SPDIFRX_VERR_MIN_MASK GENMASK(3, 0) +#define SPDIFRX_VERR_MAJ_MASK GENMASK(7, 4) + +/* Bit definition for SPDIFRX_IDR register */ +#define SPDIFRX_IDR_ID_MASK GENMASK(31, 0) + +/* Bit definition for SPDIFRX_SIDR register */ +#define SPDIFRX_SIDR_SID_MASK GENMASK(31, 0) + +#define SPDIFRX_IPIDR_NUMBER 0x00130041 + #define SPDIFRX_IN1 0x1 #define SPDIFRX_IN2 0x2 #define SPDIFRX_IN3 0x3 @@ -597,6 +613,9 @@ static bool stm32_spdifrx_readable_reg(struct device *dev, unsigned int reg) case STM32_SPDIFRX_DR: case STM32_SPDIFRX_CSR: case STM32_SPDIFRX_DIR: + case STM32_SPDIFRX_VERR: + case STM32_SPDIFRX_IDR: + case STM32_SPDIFRX_SIDR: return true; default: return false; @@ -632,10 +651,11 @@ static const struct regmap_config stm32_h7_spdifrx_regmap_conf = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, - .max_register = STM32_SPDIFRX_DIR, + .max_register = STM32_SPDIFRX_SIDR, .readable_reg = stm32_spdifrx_readable_reg, .volatile_reg = stm32_spdifrx_volatile_reg, .writeable_reg = stm32_spdifrx_writeable_reg, + .num_reg_defaults_raw = STM32_SPDIFRX_SIDR / sizeof(u32) + 1, .fast_io = true, .cache_type = REGCACHE_FLAT, }; @@ -902,6 +922,7 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) struct stm32_spdifrx_data *spdifrx; struct reset_control *rst; const struct snd_dmaengine_pcm_config *pcm_config = NULL; + u32 ver, idr; int ret; spdifrx = devm_kzalloc(&pdev->dev, sizeof(*spdifrx), GFP_KERNEL); @@ -958,7 +979,19 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) goto error; } - return 0; + ret = regmap_read(spdifrx->regmap, STM32_SPDIFRX_IDR, &idr); + if (ret) + goto error; + + if (idr == SPDIFRX_IPIDR_NUMBER) { + ret = regmap_read(spdifrx->regmap, STM32_SPDIFRX_VERR, &ver); + + dev_dbg(&pdev->dev, "SPDIFRX version: %lu.%lu registered\n", + FIELD_GET(SPDIFRX_VERR_MAJ_MASK, ver), + FIELD_GET(SPDIFRX_VERR_MIN_MASK, ver)); + } + + return ret; error: if (!IS_ERR(spdifrx->ctrl_chan)) |