18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * STM32 ALSA SoC Digital Audio Interface (SAI) driver. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2016, STMicroelectronics - All Rights Reserved 68c2ecf20Sopenharmony_ci * Author(s): Olivier Moysan <olivier.moysan@st.com> for STMicroelectronics. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/clk.h> 108c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 148c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 158c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 168c2ecf20Sopenharmony_ci#include <linux/regmap.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <sound/asoundef.h> 198c2ecf20Sopenharmony_ci#include <sound/core.h> 208c2ecf20Sopenharmony_ci#include <sound/dmaengine_pcm.h> 218c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "stm32_sai.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define SAI_FREE_PROTOCOL 0x0 268c2ecf20Sopenharmony_ci#define SAI_SPDIF_PROTOCOL 0x1 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define SAI_SLOT_SIZE_AUTO 0x0 298c2ecf20Sopenharmony_ci#define SAI_SLOT_SIZE_16 0x1 308c2ecf20Sopenharmony_ci#define SAI_SLOT_SIZE_32 0x2 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define SAI_DATASIZE_8 0x2 338c2ecf20Sopenharmony_ci#define SAI_DATASIZE_10 0x3 348c2ecf20Sopenharmony_ci#define SAI_DATASIZE_16 0x4 358c2ecf20Sopenharmony_ci#define SAI_DATASIZE_20 0x5 368c2ecf20Sopenharmony_ci#define SAI_DATASIZE_24 0x6 378c2ecf20Sopenharmony_ci#define SAI_DATASIZE_32 0x7 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define STM_SAI_DAI_NAME_SIZE 15 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define STM_SAI_IS_PLAYBACK(ip) ((ip)->dir == SNDRV_PCM_STREAM_PLAYBACK) 428c2ecf20Sopenharmony_ci#define STM_SAI_IS_CAPTURE(ip) ((ip)->dir == SNDRV_PCM_STREAM_CAPTURE) 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define STM_SAI_A_ID 0x0 458c2ecf20Sopenharmony_ci#define STM_SAI_B_ID 0x1 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define STM_SAI_IS_SUB_A(x) ((x)->id == STM_SAI_A_ID) 488c2ecf20Sopenharmony_ci#define STM_SAI_IS_SUB_B(x) ((x)->id == STM_SAI_B_ID) 498c2ecf20Sopenharmony_ci#define STM_SAI_BLOCK_NAME(x) (((x)->id == STM_SAI_A_ID) ? "A" : "B") 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define SAI_SYNC_NONE 0x0 528c2ecf20Sopenharmony_ci#define SAI_SYNC_INTERNAL 0x1 538c2ecf20Sopenharmony_ci#define SAI_SYNC_EXTERNAL 0x2 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define STM_SAI_PROTOCOL_IS_SPDIF(ip) ((ip)->spdif) 568c2ecf20Sopenharmony_ci#define STM_SAI_HAS_SPDIF(x) ((x)->pdata->conf.has_spdif_pdm) 578c2ecf20Sopenharmony_ci#define STM_SAI_HAS_PDM(x) ((x)->pdata->conf.has_spdif_pdm) 588c2ecf20Sopenharmony_ci#define STM_SAI_HAS_EXT_SYNC(x) (!STM_SAI_IS_F4(sai->pdata)) 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define SAI_IEC60958_BLOCK_FRAMES 192 618c2ecf20Sopenharmony_ci#define SAI_IEC60958_STATUS_BYTES 24 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#define SAI_MCLK_NAME_LEN 32 648c2ecf20Sopenharmony_ci#define SAI_RATE_11K 11025 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/** 678c2ecf20Sopenharmony_ci * struct stm32_sai_sub_data - private data of SAI sub block (block A or B) 688c2ecf20Sopenharmony_ci * @pdev: device data pointer 698c2ecf20Sopenharmony_ci * @regmap: SAI register map pointer 708c2ecf20Sopenharmony_ci * @regmap_config: SAI sub block register map configuration pointer 718c2ecf20Sopenharmony_ci * @dma_params: dma configuration data for rx or tx channel 728c2ecf20Sopenharmony_ci * @cpu_dai_drv: DAI driver data pointer 738c2ecf20Sopenharmony_ci * @cpu_dai: DAI runtime data pointer 748c2ecf20Sopenharmony_ci * @substream: PCM substream data pointer 758c2ecf20Sopenharmony_ci * @pdata: SAI block parent data pointer 768c2ecf20Sopenharmony_ci * @np_sync_provider: synchronization provider node 778c2ecf20Sopenharmony_ci * @sai_ck: kernel clock feeding the SAI clock generator 788c2ecf20Sopenharmony_ci * @sai_mclk: master clock from SAI mclk provider 798c2ecf20Sopenharmony_ci * @phys_addr: SAI registers physical base address 808c2ecf20Sopenharmony_ci * @mclk_rate: SAI block master clock frequency (Hz). set at init 818c2ecf20Sopenharmony_ci * @id: SAI sub block id corresponding to sub-block A or B 828c2ecf20Sopenharmony_ci * @dir: SAI block direction (playback or capture). set at init 838c2ecf20Sopenharmony_ci * @master: SAI block mode flag. (true=master, false=slave) set at init 848c2ecf20Sopenharmony_ci * @spdif: SAI S/PDIF iec60958 mode flag. set at init 858c2ecf20Sopenharmony_ci * @fmt: SAI block format. relevant only for custom protocols. set at init 868c2ecf20Sopenharmony_ci * @sync: SAI block synchronization mode. (none, internal or external) 878c2ecf20Sopenharmony_ci * @synco: SAI block ext sync source (provider setting). (none, sub-block A/B) 888c2ecf20Sopenharmony_ci * @synci: SAI block ext sync source (client setting). (SAI sync provider index) 898c2ecf20Sopenharmony_ci * @fs_length: frame synchronization length. depends on protocol settings 908c2ecf20Sopenharmony_ci * @slots: rx or tx slot number 918c2ecf20Sopenharmony_ci * @slot_width: rx or tx slot width in bits 928c2ecf20Sopenharmony_ci * @slot_mask: rx or tx active slots mask. set at init or at runtime 938c2ecf20Sopenharmony_ci * @data_size: PCM data width. corresponds to PCM substream width. 948c2ecf20Sopenharmony_ci * @spdif_frm_cnt: S/PDIF playback frame counter 958c2ecf20Sopenharmony_ci * @iec958: iec958 data 968c2ecf20Sopenharmony_ci * @ctrl_lock: control lock 978c2ecf20Sopenharmony_ci * @irq_lock: prevent race condition with IRQ 988c2ecf20Sopenharmony_ci */ 998c2ecf20Sopenharmony_cistruct stm32_sai_sub_data { 1008c2ecf20Sopenharmony_ci struct platform_device *pdev; 1018c2ecf20Sopenharmony_ci struct regmap *regmap; 1028c2ecf20Sopenharmony_ci const struct regmap_config *regmap_config; 1038c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data dma_params; 1048c2ecf20Sopenharmony_ci struct snd_soc_dai_driver cpu_dai_drv; 1058c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai; 1068c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 1078c2ecf20Sopenharmony_ci struct stm32_sai_data *pdata; 1088c2ecf20Sopenharmony_ci struct device_node *np_sync_provider; 1098c2ecf20Sopenharmony_ci struct clk *sai_ck; 1108c2ecf20Sopenharmony_ci struct clk *sai_mclk; 1118c2ecf20Sopenharmony_ci dma_addr_t phys_addr; 1128c2ecf20Sopenharmony_ci unsigned int mclk_rate; 1138c2ecf20Sopenharmony_ci unsigned int id; 1148c2ecf20Sopenharmony_ci int dir; 1158c2ecf20Sopenharmony_ci bool master; 1168c2ecf20Sopenharmony_ci bool spdif; 1178c2ecf20Sopenharmony_ci int fmt; 1188c2ecf20Sopenharmony_ci int sync; 1198c2ecf20Sopenharmony_ci int synco; 1208c2ecf20Sopenharmony_ci int synci; 1218c2ecf20Sopenharmony_ci int fs_length; 1228c2ecf20Sopenharmony_ci int slots; 1238c2ecf20Sopenharmony_ci int slot_width; 1248c2ecf20Sopenharmony_ci int slot_mask; 1258c2ecf20Sopenharmony_ci int data_size; 1268c2ecf20Sopenharmony_ci unsigned int spdif_frm_cnt; 1278c2ecf20Sopenharmony_ci struct snd_aes_iec958 iec958; 1288c2ecf20Sopenharmony_ci struct mutex ctrl_lock; /* protect resources accessed by controls */ 1298c2ecf20Sopenharmony_ci spinlock_t irq_lock; /* used to prevent race condition with IRQ */ 1308c2ecf20Sopenharmony_ci}; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cienum stm32_sai_fifo_th { 1338c2ecf20Sopenharmony_ci STM_SAI_FIFO_TH_EMPTY, 1348c2ecf20Sopenharmony_ci STM_SAI_FIFO_TH_QUARTER, 1358c2ecf20Sopenharmony_ci STM_SAI_FIFO_TH_HALF, 1368c2ecf20Sopenharmony_ci STM_SAI_FIFO_TH_3_QUARTER, 1378c2ecf20Sopenharmony_ci STM_SAI_FIFO_TH_FULL, 1388c2ecf20Sopenharmony_ci}; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic bool stm32_sai_sub_readable_reg(struct device *dev, unsigned int reg) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci switch (reg) { 1438c2ecf20Sopenharmony_ci case STM_SAI_CR1_REGX: 1448c2ecf20Sopenharmony_ci case STM_SAI_CR2_REGX: 1458c2ecf20Sopenharmony_ci case STM_SAI_FRCR_REGX: 1468c2ecf20Sopenharmony_ci case STM_SAI_SLOTR_REGX: 1478c2ecf20Sopenharmony_ci case STM_SAI_IMR_REGX: 1488c2ecf20Sopenharmony_ci case STM_SAI_SR_REGX: 1498c2ecf20Sopenharmony_ci case STM_SAI_CLRFR_REGX: 1508c2ecf20Sopenharmony_ci case STM_SAI_DR_REGX: 1518c2ecf20Sopenharmony_ci case STM_SAI_PDMCR_REGX: 1528c2ecf20Sopenharmony_ci case STM_SAI_PDMLY_REGX: 1538c2ecf20Sopenharmony_ci return true; 1548c2ecf20Sopenharmony_ci default: 1558c2ecf20Sopenharmony_ci return false; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic bool stm32_sai_sub_volatile_reg(struct device *dev, unsigned int reg) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci switch (reg) { 1628c2ecf20Sopenharmony_ci case STM_SAI_DR_REGX: 1638c2ecf20Sopenharmony_ci case STM_SAI_SR_REGX: 1648c2ecf20Sopenharmony_ci return true; 1658c2ecf20Sopenharmony_ci default: 1668c2ecf20Sopenharmony_ci return false; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic bool stm32_sai_sub_writeable_reg(struct device *dev, unsigned int reg) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci switch (reg) { 1738c2ecf20Sopenharmony_ci case STM_SAI_CR1_REGX: 1748c2ecf20Sopenharmony_ci case STM_SAI_CR2_REGX: 1758c2ecf20Sopenharmony_ci case STM_SAI_FRCR_REGX: 1768c2ecf20Sopenharmony_ci case STM_SAI_SLOTR_REGX: 1778c2ecf20Sopenharmony_ci case STM_SAI_IMR_REGX: 1788c2ecf20Sopenharmony_ci case STM_SAI_CLRFR_REGX: 1798c2ecf20Sopenharmony_ci case STM_SAI_DR_REGX: 1808c2ecf20Sopenharmony_ci case STM_SAI_PDMCR_REGX: 1818c2ecf20Sopenharmony_ci case STM_SAI_PDMLY_REGX: 1828c2ecf20Sopenharmony_ci return true; 1838c2ecf20Sopenharmony_ci default: 1848c2ecf20Sopenharmony_ci return false; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic int stm32_sai_sub_reg_up(struct stm32_sai_sub_data *sai, 1898c2ecf20Sopenharmony_ci unsigned int reg, unsigned int mask, 1908c2ecf20Sopenharmony_ci unsigned int val) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci int ret; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci ret = clk_enable(sai->pdata->pclk); 1958c2ecf20Sopenharmony_ci if (ret < 0) 1968c2ecf20Sopenharmony_ci return ret; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci ret = regmap_update_bits(sai->regmap, reg, mask, val); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci clk_disable(sai->pdata->pclk); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci return ret; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic int stm32_sai_sub_reg_wr(struct stm32_sai_sub_data *sai, 2068c2ecf20Sopenharmony_ci unsigned int reg, unsigned int mask, 2078c2ecf20Sopenharmony_ci unsigned int val) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci int ret; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci ret = clk_enable(sai->pdata->pclk); 2128c2ecf20Sopenharmony_ci if (ret < 0) 2138c2ecf20Sopenharmony_ci return ret; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci ret = regmap_write_bits(sai->regmap, reg, mask, val); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci clk_disable(sai->pdata->pclk); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci return ret; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic int stm32_sai_sub_reg_rd(struct stm32_sai_sub_data *sai, 2238c2ecf20Sopenharmony_ci unsigned int reg, unsigned int *val) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci int ret; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci ret = clk_enable(sai->pdata->pclk); 2288c2ecf20Sopenharmony_ci if (ret < 0) 2298c2ecf20Sopenharmony_ci return ret; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci ret = regmap_read(sai->regmap, reg, val); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci clk_disable(sai->pdata->pclk); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return ret; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic const struct regmap_config stm32_sai_sub_regmap_config_f4 = { 2398c2ecf20Sopenharmony_ci .reg_bits = 32, 2408c2ecf20Sopenharmony_ci .reg_stride = 4, 2418c2ecf20Sopenharmony_ci .val_bits = 32, 2428c2ecf20Sopenharmony_ci .max_register = STM_SAI_DR_REGX, 2438c2ecf20Sopenharmony_ci .readable_reg = stm32_sai_sub_readable_reg, 2448c2ecf20Sopenharmony_ci .volatile_reg = stm32_sai_sub_volatile_reg, 2458c2ecf20Sopenharmony_ci .writeable_reg = stm32_sai_sub_writeable_reg, 2468c2ecf20Sopenharmony_ci .fast_io = true, 2478c2ecf20Sopenharmony_ci .cache_type = REGCACHE_FLAT, 2488c2ecf20Sopenharmony_ci}; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic const struct regmap_config stm32_sai_sub_regmap_config_h7 = { 2518c2ecf20Sopenharmony_ci .reg_bits = 32, 2528c2ecf20Sopenharmony_ci .reg_stride = 4, 2538c2ecf20Sopenharmony_ci .val_bits = 32, 2548c2ecf20Sopenharmony_ci .max_register = STM_SAI_PDMLY_REGX, 2558c2ecf20Sopenharmony_ci .readable_reg = stm32_sai_sub_readable_reg, 2568c2ecf20Sopenharmony_ci .volatile_reg = stm32_sai_sub_volatile_reg, 2578c2ecf20Sopenharmony_ci .writeable_reg = stm32_sai_sub_writeable_reg, 2588c2ecf20Sopenharmony_ci .fast_io = true, 2598c2ecf20Sopenharmony_ci .cache_type = REGCACHE_FLAT, 2608c2ecf20Sopenharmony_ci}; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic int snd_pcm_iec958_info(struct snd_kcontrol *kcontrol, 2638c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; 2668c2ecf20Sopenharmony_ci uinfo->count = 1; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci return 0; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic int snd_pcm_iec958_get(struct snd_kcontrol *kcontrol, 2728c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *uctl) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci struct stm32_sai_sub_data *sai = snd_kcontrol_chip(kcontrol); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci mutex_lock(&sai->ctrl_lock); 2778c2ecf20Sopenharmony_ci memcpy(uctl->value.iec958.status, sai->iec958.status, 4); 2788c2ecf20Sopenharmony_ci mutex_unlock(&sai->ctrl_lock); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci return 0; 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic int snd_pcm_iec958_put(struct snd_kcontrol *kcontrol, 2848c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *uctl) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci struct stm32_sai_sub_data *sai = snd_kcontrol_chip(kcontrol); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci mutex_lock(&sai->ctrl_lock); 2898c2ecf20Sopenharmony_ci memcpy(sai->iec958.status, uctl->value.iec958.status, 4); 2908c2ecf20Sopenharmony_ci mutex_unlock(&sai->ctrl_lock); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci return 0; 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new iec958_ctls = { 2968c2ecf20Sopenharmony_ci .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | 2978c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_VOLATILE), 2988c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 2998c2ecf20Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), 3008c2ecf20Sopenharmony_ci .info = snd_pcm_iec958_info, 3018c2ecf20Sopenharmony_ci .get = snd_pcm_iec958_get, 3028c2ecf20Sopenharmony_ci .put = snd_pcm_iec958_put, 3038c2ecf20Sopenharmony_ci}; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistruct stm32_sai_mclk_data { 3068c2ecf20Sopenharmony_ci struct clk_hw hw; 3078c2ecf20Sopenharmony_ci unsigned long freq; 3088c2ecf20Sopenharmony_ci struct stm32_sai_sub_data *sai_data; 3098c2ecf20Sopenharmony_ci}; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci#define to_mclk_data(_hw) container_of(_hw, struct stm32_sai_mclk_data, hw) 3128c2ecf20Sopenharmony_ci#define STM32_SAI_MAX_CLKS 1 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic int stm32_sai_get_clk_div(struct stm32_sai_sub_data *sai, 3158c2ecf20Sopenharmony_ci unsigned long input_rate, 3168c2ecf20Sopenharmony_ci unsigned long output_rate) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci int version = sai->pdata->conf.version; 3198c2ecf20Sopenharmony_ci int div; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci div = DIV_ROUND_CLOSEST(input_rate, output_rate); 3228c2ecf20Sopenharmony_ci if (div > SAI_XCR1_MCKDIV_MAX(version)) { 3238c2ecf20Sopenharmony_ci dev_err(&sai->pdev->dev, "Divider %d out of range\n", div); 3248c2ecf20Sopenharmony_ci return -EINVAL; 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci dev_dbg(&sai->pdev->dev, "SAI divider %d\n", div); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci if (input_rate % div) 3298c2ecf20Sopenharmony_ci dev_dbg(&sai->pdev->dev, 3308c2ecf20Sopenharmony_ci "Rate not accurate. requested (%ld), actual (%ld)\n", 3318c2ecf20Sopenharmony_ci output_rate, input_rate / div); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci return div; 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic int stm32_sai_set_clk_div(struct stm32_sai_sub_data *sai, 3378c2ecf20Sopenharmony_ci unsigned int div) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci int version = sai->pdata->conf.version; 3408c2ecf20Sopenharmony_ci int ret, cr1, mask; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if (div > SAI_XCR1_MCKDIV_MAX(version)) { 3438c2ecf20Sopenharmony_ci dev_err(&sai->pdev->dev, "Divider %d out of range\n", div); 3448c2ecf20Sopenharmony_ci return -EINVAL; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci mask = SAI_XCR1_MCKDIV_MASK(SAI_XCR1_MCKDIV_WIDTH(version)); 3488c2ecf20Sopenharmony_ci cr1 = SAI_XCR1_MCKDIV_SET(div); 3498c2ecf20Sopenharmony_ci ret = stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX, mask, cr1); 3508c2ecf20Sopenharmony_ci if (ret < 0) 3518c2ecf20Sopenharmony_ci dev_err(&sai->pdev->dev, "Failed to update CR1 register\n"); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci return ret; 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic int stm32_sai_set_parent_clock(struct stm32_sai_sub_data *sai, 3578c2ecf20Sopenharmony_ci unsigned int rate) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci struct platform_device *pdev = sai->pdev; 3608c2ecf20Sopenharmony_ci struct clk *parent_clk = sai->pdata->clk_x8k; 3618c2ecf20Sopenharmony_ci int ret; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci if (!(rate % SAI_RATE_11K)) 3648c2ecf20Sopenharmony_ci parent_clk = sai->pdata->clk_x11k; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci ret = clk_set_parent(sai->sai_ck, parent_clk); 3678c2ecf20Sopenharmony_ci if (ret) 3688c2ecf20Sopenharmony_ci dev_err(&pdev->dev, " Error %d setting sai_ck parent clock. %s", 3698c2ecf20Sopenharmony_ci ret, ret == -EBUSY ? 3708c2ecf20Sopenharmony_ci "Active stream rates conflict\n" : "\n"); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci return ret; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic long stm32_sai_mclk_round_rate(struct clk_hw *hw, unsigned long rate, 3768c2ecf20Sopenharmony_ci unsigned long *prate) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci struct stm32_sai_mclk_data *mclk = to_mclk_data(hw); 3798c2ecf20Sopenharmony_ci struct stm32_sai_sub_data *sai = mclk->sai_data; 3808c2ecf20Sopenharmony_ci int div; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci div = stm32_sai_get_clk_div(sai, *prate, rate); 3838c2ecf20Sopenharmony_ci if (div < 0) 3848c2ecf20Sopenharmony_ci return div; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci mclk->freq = *prate / div; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci return mclk->freq; 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic unsigned long stm32_sai_mclk_recalc_rate(struct clk_hw *hw, 3928c2ecf20Sopenharmony_ci unsigned long parent_rate) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci struct stm32_sai_mclk_data *mclk = to_mclk_data(hw); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci return mclk->freq; 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic int stm32_sai_mclk_set_rate(struct clk_hw *hw, unsigned long rate, 4008c2ecf20Sopenharmony_ci unsigned long parent_rate) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci struct stm32_sai_mclk_data *mclk = to_mclk_data(hw); 4038c2ecf20Sopenharmony_ci struct stm32_sai_sub_data *sai = mclk->sai_data; 4048c2ecf20Sopenharmony_ci int div, ret; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci div = stm32_sai_get_clk_div(sai, parent_rate, rate); 4078c2ecf20Sopenharmony_ci if (div < 0) 4088c2ecf20Sopenharmony_ci return div; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci ret = stm32_sai_set_clk_div(sai, div); 4118c2ecf20Sopenharmony_ci if (ret) 4128c2ecf20Sopenharmony_ci return ret; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci mclk->freq = rate; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci return 0; 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_cistatic int stm32_sai_mclk_enable(struct clk_hw *hw) 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci struct stm32_sai_mclk_data *mclk = to_mclk_data(hw); 4228c2ecf20Sopenharmony_ci struct stm32_sai_sub_data *sai = mclk->sai_data; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci dev_dbg(&sai->pdev->dev, "Enable master clock\n"); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci return stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX, 4278c2ecf20Sopenharmony_ci SAI_XCR1_MCKEN, SAI_XCR1_MCKEN); 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic void stm32_sai_mclk_disable(struct clk_hw *hw) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci struct stm32_sai_mclk_data *mclk = to_mclk_data(hw); 4338c2ecf20Sopenharmony_ci struct stm32_sai_sub_data *sai = mclk->sai_data; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci dev_dbg(&sai->pdev->dev, "Disable master clock\n"); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX, SAI_XCR1_MCKEN, 0); 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_cistatic const struct clk_ops mclk_ops = { 4418c2ecf20Sopenharmony_ci .enable = stm32_sai_mclk_enable, 4428c2ecf20Sopenharmony_ci .disable = stm32_sai_mclk_disable, 4438c2ecf20Sopenharmony_ci .recalc_rate = stm32_sai_mclk_recalc_rate, 4448c2ecf20Sopenharmony_ci .round_rate = stm32_sai_mclk_round_rate, 4458c2ecf20Sopenharmony_ci .set_rate = stm32_sai_mclk_set_rate, 4468c2ecf20Sopenharmony_ci}; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic int stm32_sai_add_mclk_provider(struct stm32_sai_sub_data *sai) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci struct clk_hw *hw; 4518c2ecf20Sopenharmony_ci struct stm32_sai_mclk_data *mclk; 4528c2ecf20Sopenharmony_ci struct device *dev = &sai->pdev->dev; 4538c2ecf20Sopenharmony_ci const char *pname = __clk_get_name(sai->sai_ck); 4548c2ecf20Sopenharmony_ci char *mclk_name, *p, *s = (char *)pname; 4558c2ecf20Sopenharmony_ci int ret, i = 0; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci mclk = devm_kzalloc(dev, sizeof(*mclk), GFP_KERNEL); 4588c2ecf20Sopenharmony_ci if (!mclk) 4598c2ecf20Sopenharmony_ci return -ENOMEM; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci mclk_name = devm_kcalloc(dev, sizeof(char), 4628c2ecf20Sopenharmony_ci SAI_MCLK_NAME_LEN, GFP_KERNEL); 4638c2ecf20Sopenharmony_ci if (!mclk_name) 4648c2ecf20Sopenharmony_ci return -ENOMEM; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci /* 4678c2ecf20Sopenharmony_ci * Forge mclk clock name from parent clock name and suffix. 4688c2ecf20Sopenharmony_ci * String after "_" char is stripped in parent name. 4698c2ecf20Sopenharmony_ci */ 4708c2ecf20Sopenharmony_ci p = mclk_name; 4718c2ecf20Sopenharmony_ci while (*s && *s != '_' && (i < (SAI_MCLK_NAME_LEN - 7))) { 4728c2ecf20Sopenharmony_ci *p++ = *s++; 4738c2ecf20Sopenharmony_ci i++; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci STM_SAI_IS_SUB_A(sai) ? strcat(p, "a_mclk") : strcat(p, "b_mclk"); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci mclk->hw.init = CLK_HW_INIT(mclk_name, pname, &mclk_ops, 0); 4788c2ecf20Sopenharmony_ci mclk->sai_data = sai; 4798c2ecf20Sopenharmony_ci hw = &mclk->hw; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci dev_dbg(dev, "Register master clock %s\n", mclk_name); 4828c2ecf20Sopenharmony_ci ret = devm_clk_hw_register(&sai->pdev->dev, hw); 4838c2ecf20Sopenharmony_ci if (ret) { 4848c2ecf20Sopenharmony_ci dev_err(dev, "mclk register returned %d\n", ret); 4858c2ecf20Sopenharmony_ci return ret; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci sai->sai_mclk = hw->clk; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* register mclk provider */ 4908c2ecf20Sopenharmony_ci return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw); 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_cistatic irqreturn_t stm32_sai_isr(int irq, void *devid) 4948c2ecf20Sopenharmony_ci{ 4958c2ecf20Sopenharmony_ci struct stm32_sai_sub_data *sai = (struct stm32_sai_sub_data *)devid; 4968c2ecf20Sopenharmony_ci struct platform_device *pdev = sai->pdev; 4978c2ecf20Sopenharmony_ci unsigned int sr, imr, flags; 4988c2ecf20Sopenharmony_ci snd_pcm_state_t status = SNDRV_PCM_STATE_RUNNING; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci stm32_sai_sub_reg_rd(sai, STM_SAI_IMR_REGX, &imr); 5018c2ecf20Sopenharmony_ci stm32_sai_sub_reg_rd(sai, STM_SAI_SR_REGX, &sr); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci flags = sr & imr; 5048c2ecf20Sopenharmony_ci if (!flags) 5058c2ecf20Sopenharmony_ci return IRQ_NONE; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci stm32_sai_sub_reg_wr(sai, STM_SAI_CLRFR_REGX, SAI_XCLRFR_MASK, 5088c2ecf20Sopenharmony_ci SAI_XCLRFR_MASK); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci if (!sai->substream) { 5118c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Device stopped. Spurious IRQ 0x%x\n", sr); 5128c2ecf20Sopenharmony_ci return IRQ_NONE; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci if (flags & SAI_XIMR_OVRUDRIE) { 5168c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "IRQ %s\n", 5178c2ecf20Sopenharmony_ci STM_SAI_IS_PLAYBACK(sai) ? "underrun" : "overrun"); 5188c2ecf20Sopenharmony_ci status = SNDRV_PCM_STATE_XRUN; 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci if (flags & SAI_XIMR_MUTEDETIE) 5228c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "IRQ mute detected\n"); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci if (flags & SAI_XIMR_WCKCFGIE) { 5258c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "IRQ wrong clock configuration\n"); 5268c2ecf20Sopenharmony_ci status = SNDRV_PCM_STATE_DISCONNECTED; 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci if (flags & SAI_XIMR_CNRDYIE) 5308c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "IRQ Codec not ready\n"); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (flags & SAI_XIMR_AFSDETIE) { 5338c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "IRQ Anticipated frame synchro\n"); 5348c2ecf20Sopenharmony_ci status = SNDRV_PCM_STATE_XRUN; 5358c2ecf20Sopenharmony_ci } 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci if (flags & SAI_XIMR_LFSDETIE) { 5388c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "IRQ Late frame synchro\n"); 5398c2ecf20Sopenharmony_ci status = SNDRV_PCM_STATE_XRUN; 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci spin_lock(&sai->irq_lock); 5438c2ecf20Sopenharmony_ci if (status != SNDRV_PCM_STATE_RUNNING && sai->substream) 5448c2ecf20Sopenharmony_ci snd_pcm_stop_xrun(sai->substream); 5458c2ecf20Sopenharmony_ci spin_unlock(&sai->irq_lock); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic int stm32_sai_set_sysclk(struct snd_soc_dai *cpu_dai, 5518c2ecf20Sopenharmony_ci int clk_id, unsigned int freq, int dir) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); 5548c2ecf20Sopenharmony_ci int ret; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci if (dir == SND_SOC_CLOCK_OUT && sai->sai_mclk) { 5578c2ecf20Sopenharmony_ci ret = stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX, 5588c2ecf20Sopenharmony_ci SAI_XCR1_NODIV, 5598c2ecf20Sopenharmony_ci freq ? 0 : SAI_XCR1_NODIV); 5608c2ecf20Sopenharmony_ci if (ret < 0) 5618c2ecf20Sopenharmony_ci return ret; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci /* Assume shutdown if requested frequency is 0Hz */ 5648c2ecf20Sopenharmony_ci if (!freq) { 5658c2ecf20Sopenharmony_ci /* Release mclk rate only if rate was actually set */ 5668c2ecf20Sopenharmony_ci if (sai->mclk_rate) { 5678c2ecf20Sopenharmony_ci clk_rate_exclusive_put(sai->sai_mclk); 5688c2ecf20Sopenharmony_ci sai->mclk_rate = 0; 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci return 0; 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci /* If master clock is used, set parent clock now */ 5748c2ecf20Sopenharmony_ci ret = stm32_sai_set_parent_clock(sai, freq); 5758c2ecf20Sopenharmony_ci if (ret) 5768c2ecf20Sopenharmony_ci return ret; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci ret = clk_set_rate_exclusive(sai->sai_mclk, freq); 5798c2ecf20Sopenharmony_ci if (ret) { 5808c2ecf20Sopenharmony_ci dev_err(cpu_dai->dev, 5818c2ecf20Sopenharmony_ci ret == -EBUSY ? 5828c2ecf20Sopenharmony_ci "Active streams have incompatible rates" : 5838c2ecf20Sopenharmony_ci "Could not set mclk rate\n"); 5848c2ecf20Sopenharmony_ci return ret; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci dev_dbg(cpu_dai->dev, "SAI MCLK frequency is %uHz\n", freq); 5888c2ecf20Sopenharmony_ci sai->mclk_rate = freq; 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci return 0; 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_cistatic int stm32_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask, 5958c2ecf20Sopenharmony_ci u32 rx_mask, int slots, int slot_width) 5968c2ecf20Sopenharmony_ci{ 5978c2ecf20Sopenharmony_ci struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); 5988c2ecf20Sopenharmony_ci int slotr, slotr_mask, slot_size; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) { 6018c2ecf20Sopenharmony_ci dev_warn(cpu_dai->dev, "Slot setting relevant only for TDM\n"); 6028c2ecf20Sopenharmony_ci return 0; 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci dev_dbg(cpu_dai->dev, "Masks tx/rx:%#x/%#x, slots:%d, width:%d\n", 6068c2ecf20Sopenharmony_ci tx_mask, rx_mask, slots, slot_width); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci switch (slot_width) { 6098c2ecf20Sopenharmony_ci case 16: 6108c2ecf20Sopenharmony_ci slot_size = SAI_SLOT_SIZE_16; 6118c2ecf20Sopenharmony_ci break; 6128c2ecf20Sopenharmony_ci case 32: 6138c2ecf20Sopenharmony_ci slot_size = SAI_SLOT_SIZE_32; 6148c2ecf20Sopenharmony_ci break; 6158c2ecf20Sopenharmony_ci default: 6168c2ecf20Sopenharmony_ci slot_size = SAI_SLOT_SIZE_AUTO; 6178c2ecf20Sopenharmony_ci break; 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci slotr = SAI_XSLOTR_SLOTSZ_SET(slot_size) | 6218c2ecf20Sopenharmony_ci SAI_XSLOTR_NBSLOT_SET(slots - 1); 6228c2ecf20Sopenharmony_ci slotr_mask = SAI_XSLOTR_SLOTSZ_MASK | SAI_XSLOTR_NBSLOT_MASK; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci /* tx/rx mask set in machine init, if slot number defined in DT */ 6258c2ecf20Sopenharmony_ci if (STM_SAI_IS_PLAYBACK(sai)) { 6268c2ecf20Sopenharmony_ci sai->slot_mask = tx_mask; 6278c2ecf20Sopenharmony_ci slotr |= SAI_XSLOTR_SLOTEN_SET(tx_mask); 6288c2ecf20Sopenharmony_ci } 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci if (STM_SAI_IS_CAPTURE(sai)) { 6318c2ecf20Sopenharmony_ci sai->slot_mask = rx_mask; 6328c2ecf20Sopenharmony_ci slotr |= SAI_XSLOTR_SLOTEN_SET(rx_mask); 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci slotr_mask |= SAI_XSLOTR_SLOTEN_MASK; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci stm32_sai_sub_reg_up(sai, STM_SAI_SLOTR_REGX, slotr_mask, slotr); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci sai->slot_width = slot_width; 6408c2ecf20Sopenharmony_ci sai->slots = slots; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci return 0; 6438c2ecf20Sopenharmony_ci} 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_cistatic int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); 6488c2ecf20Sopenharmony_ci int cr1, frcr = 0; 6498c2ecf20Sopenharmony_ci int cr1_mask, frcr_mask = 0; 6508c2ecf20Sopenharmony_ci int ret; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci dev_dbg(cpu_dai->dev, "fmt %x\n", fmt); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci /* Do not generate master by default */ 6558c2ecf20Sopenharmony_ci cr1 = SAI_XCR1_NODIV; 6568c2ecf20Sopenharmony_ci cr1_mask = SAI_XCR1_NODIV; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci cr1_mask |= SAI_XCR1_PRTCFG_MASK; 6598c2ecf20Sopenharmony_ci if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) { 6608c2ecf20Sopenharmony_ci cr1 |= SAI_XCR1_PRTCFG_SET(SAI_SPDIF_PROTOCOL); 6618c2ecf20Sopenharmony_ci goto conf_update; 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci cr1 |= SAI_XCR1_PRTCFG_SET(SAI_FREE_PROTOCOL); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 6678c2ecf20Sopenharmony_ci /* SCK active high for all protocols */ 6688c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 6698c2ecf20Sopenharmony_ci cr1 |= SAI_XCR1_CKSTR; 6708c2ecf20Sopenharmony_ci frcr |= SAI_XFRCR_FSOFF | SAI_XFRCR_FSDEF; 6718c2ecf20Sopenharmony_ci break; 6728c2ecf20Sopenharmony_ci /* Left justified */ 6738c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_MSB: 6748c2ecf20Sopenharmony_ci frcr |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSDEF; 6758c2ecf20Sopenharmony_ci break; 6768c2ecf20Sopenharmony_ci /* Right justified */ 6778c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_LSB: 6788c2ecf20Sopenharmony_ci frcr |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSDEF; 6798c2ecf20Sopenharmony_ci break; 6808c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A: 6818c2ecf20Sopenharmony_ci frcr |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSOFF; 6828c2ecf20Sopenharmony_ci break; 6838c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_B: 6848c2ecf20Sopenharmony_ci frcr |= SAI_XFRCR_FSPOL; 6858c2ecf20Sopenharmony_ci break; 6868c2ecf20Sopenharmony_ci default: 6878c2ecf20Sopenharmony_ci dev_err(cpu_dai->dev, "Unsupported protocol %#x\n", 6888c2ecf20Sopenharmony_ci fmt & SND_SOC_DAIFMT_FORMAT_MASK); 6898c2ecf20Sopenharmony_ci return -EINVAL; 6908c2ecf20Sopenharmony_ci } 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci cr1_mask |= SAI_XCR1_CKSTR; 6938c2ecf20Sopenharmony_ci frcr_mask |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSOFF | 6948c2ecf20Sopenharmony_ci SAI_XFRCR_FSDEF; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci /* DAI clock strobing. Invert setting previously set */ 6978c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 6988c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_NB_NF: 6998c2ecf20Sopenharmony_ci break; 7008c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_IB_NF: 7018c2ecf20Sopenharmony_ci cr1 ^= SAI_XCR1_CKSTR; 7028c2ecf20Sopenharmony_ci break; 7038c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_NB_IF: 7048c2ecf20Sopenharmony_ci frcr ^= SAI_XFRCR_FSPOL; 7058c2ecf20Sopenharmony_ci break; 7068c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_IB_IF: 7078c2ecf20Sopenharmony_ci /* Invert fs & sck */ 7088c2ecf20Sopenharmony_ci cr1 ^= SAI_XCR1_CKSTR; 7098c2ecf20Sopenharmony_ci frcr ^= SAI_XFRCR_FSPOL; 7108c2ecf20Sopenharmony_ci break; 7118c2ecf20Sopenharmony_ci default: 7128c2ecf20Sopenharmony_ci dev_err(cpu_dai->dev, "Unsupported strobing %#x\n", 7138c2ecf20Sopenharmony_ci fmt & SND_SOC_DAIFMT_INV_MASK); 7148c2ecf20Sopenharmony_ci return -EINVAL; 7158c2ecf20Sopenharmony_ci } 7168c2ecf20Sopenharmony_ci cr1_mask |= SAI_XCR1_CKSTR; 7178c2ecf20Sopenharmony_ci frcr_mask |= SAI_XFRCR_FSPOL; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci stm32_sai_sub_reg_up(sai, STM_SAI_FRCR_REGX, frcr_mask, frcr); 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci /* DAI clock master masks */ 7228c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 7238c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFM: 7248c2ecf20Sopenharmony_ci /* codec is master */ 7258c2ecf20Sopenharmony_ci cr1 |= SAI_XCR1_SLAVE; 7268c2ecf20Sopenharmony_ci sai->master = false; 7278c2ecf20Sopenharmony_ci break; 7288c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFS: 7298c2ecf20Sopenharmony_ci sai->master = true; 7308c2ecf20Sopenharmony_ci break; 7318c2ecf20Sopenharmony_ci default: 7328c2ecf20Sopenharmony_ci dev_err(cpu_dai->dev, "Unsupported mode %#x\n", 7338c2ecf20Sopenharmony_ci fmt & SND_SOC_DAIFMT_MASTER_MASK); 7348c2ecf20Sopenharmony_ci return -EINVAL; 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci /* Set slave mode if sub-block is synchronized with another SAI */ 7388c2ecf20Sopenharmony_ci if (sai->sync) { 7398c2ecf20Sopenharmony_ci dev_dbg(cpu_dai->dev, "Synchronized SAI configured as slave\n"); 7408c2ecf20Sopenharmony_ci cr1 |= SAI_XCR1_SLAVE; 7418c2ecf20Sopenharmony_ci sai->master = false; 7428c2ecf20Sopenharmony_ci } 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci cr1_mask |= SAI_XCR1_SLAVE; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ciconf_update: 7478c2ecf20Sopenharmony_ci ret = stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX, cr1_mask, cr1); 7488c2ecf20Sopenharmony_ci if (ret < 0) { 7498c2ecf20Sopenharmony_ci dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); 7508c2ecf20Sopenharmony_ci return ret; 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci sai->fmt = fmt; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci return 0; 7568c2ecf20Sopenharmony_ci} 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_cistatic int stm32_sai_startup(struct snd_pcm_substream *substream, 7598c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai) 7608c2ecf20Sopenharmony_ci{ 7618c2ecf20Sopenharmony_ci struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); 7628c2ecf20Sopenharmony_ci int imr, cr2, ret; 7638c2ecf20Sopenharmony_ci unsigned long flags; 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci spin_lock_irqsave(&sai->irq_lock, flags); 7668c2ecf20Sopenharmony_ci sai->substream = substream; 7678c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sai->irq_lock, flags); 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) { 7708c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_mask64(substream->runtime, 7718c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_FORMAT, 7728c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE); 7738c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_single(substream->runtime, 7748c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS, 2); 7758c2ecf20Sopenharmony_ci } 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci ret = clk_prepare_enable(sai->sai_ck); 7788c2ecf20Sopenharmony_ci if (ret < 0) { 7798c2ecf20Sopenharmony_ci dev_err(cpu_dai->dev, "Failed to enable clock: %d\n", ret); 7808c2ecf20Sopenharmony_ci return ret; 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci /* Enable ITs */ 7848c2ecf20Sopenharmony_ci stm32_sai_sub_reg_wr(sai, STM_SAI_CLRFR_REGX, 7858c2ecf20Sopenharmony_ci SAI_XCLRFR_MASK, SAI_XCLRFR_MASK); 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci imr = SAI_XIMR_OVRUDRIE; 7888c2ecf20Sopenharmony_ci if (STM_SAI_IS_CAPTURE(sai)) { 7898c2ecf20Sopenharmony_ci stm32_sai_sub_reg_rd(sai, STM_SAI_CR2_REGX, &cr2); 7908c2ecf20Sopenharmony_ci if (cr2 & SAI_XCR2_MUTECNT_MASK) 7918c2ecf20Sopenharmony_ci imr |= SAI_XIMR_MUTEDETIE; 7928c2ecf20Sopenharmony_ci } 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci if (sai->master) 7958c2ecf20Sopenharmony_ci imr |= SAI_XIMR_WCKCFGIE; 7968c2ecf20Sopenharmony_ci else 7978c2ecf20Sopenharmony_ci imr |= SAI_XIMR_AFSDETIE | SAI_XIMR_LFSDETIE; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci stm32_sai_sub_reg_up(sai, STM_SAI_IMR_REGX, 8008c2ecf20Sopenharmony_ci SAI_XIMR_MASK, imr); 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci return 0; 8038c2ecf20Sopenharmony_ci} 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_cistatic int stm32_sai_set_config(struct snd_soc_dai *cpu_dai, 8068c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, 8078c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params) 8088c2ecf20Sopenharmony_ci{ 8098c2ecf20Sopenharmony_ci struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); 8108c2ecf20Sopenharmony_ci int cr1, cr1_mask, ret; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci /* 8138c2ecf20Sopenharmony_ci * DMA bursts increment is set to 4 words. 8148c2ecf20Sopenharmony_ci * SAI fifo threshold is set to half fifo, to keep enough space 8158c2ecf20Sopenharmony_ci * for DMA incoming bursts. 8168c2ecf20Sopenharmony_ci */ 8178c2ecf20Sopenharmony_ci stm32_sai_sub_reg_wr(sai, STM_SAI_CR2_REGX, 8188c2ecf20Sopenharmony_ci SAI_XCR2_FFLUSH | SAI_XCR2_FTH_MASK, 8198c2ecf20Sopenharmony_ci SAI_XCR2_FFLUSH | 8208c2ecf20Sopenharmony_ci SAI_XCR2_FTH_SET(STM_SAI_FIFO_TH_HALF)); 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci /* DS bits in CR1 not set for SPDIF (size forced to 24 bits).*/ 8238c2ecf20Sopenharmony_ci if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) { 8248c2ecf20Sopenharmony_ci sai->spdif_frm_cnt = 0; 8258c2ecf20Sopenharmony_ci return 0; 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci /* Mode, data format and channel config */ 8298c2ecf20Sopenharmony_ci cr1_mask = SAI_XCR1_DS_MASK; 8308c2ecf20Sopenharmony_ci switch (params_format(params)) { 8318c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S8: 8328c2ecf20Sopenharmony_ci cr1 = SAI_XCR1_DS_SET(SAI_DATASIZE_8); 8338c2ecf20Sopenharmony_ci break; 8348c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 8358c2ecf20Sopenharmony_ci cr1 = SAI_XCR1_DS_SET(SAI_DATASIZE_16); 8368c2ecf20Sopenharmony_ci break; 8378c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S32_LE: 8388c2ecf20Sopenharmony_ci cr1 = SAI_XCR1_DS_SET(SAI_DATASIZE_32); 8398c2ecf20Sopenharmony_ci break; 8408c2ecf20Sopenharmony_ci default: 8418c2ecf20Sopenharmony_ci dev_err(cpu_dai->dev, "Data format not supported\n"); 8428c2ecf20Sopenharmony_ci return -EINVAL; 8438c2ecf20Sopenharmony_ci } 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci cr1_mask |= SAI_XCR1_MONO; 8468c2ecf20Sopenharmony_ci if ((sai->slots == 2) && (params_channels(params) == 1)) 8478c2ecf20Sopenharmony_ci cr1 |= SAI_XCR1_MONO; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci ret = stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX, cr1_mask, cr1); 8508c2ecf20Sopenharmony_ci if (ret < 0) { 8518c2ecf20Sopenharmony_ci dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); 8528c2ecf20Sopenharmony_ci return ret; 8538c2ecf20Sopenharmony_ci } 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci return 0; 8568c2ecf20Sopenharmony_ci} 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_cistatic int stm32_sai_set_slots(struct snd_soc_dai *cpu_dai) 8598c2ecf20Sopenharmony_ci{ 8608c2ecf20Sopenharmony_ci struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); 8618c2ecf20Sopenharmony_ci int slotr, slot_sz; 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci stm32_sai_sub_reg_rd(sai, STM_SAI_SLOTR_REGX, &slotr); 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci /* 8668c2ecf20Sopenharmony_ci * If SLOTSZ is set to auto in SLOTR, align slot width on data size 8678c2ecf20Sopenharmony_ci * By default slot width = data size, if not forced from DT 8688c2ecf20Sopenharmony_ci */ 8698c2ecf20Sopenharmony_ci slot_sz = slotr & SAI_XSLOTR_SLOTSZ_MASK; 8708c2ecf20Sopenharmony_ci if (slot_sz == SAI_XSLOTR_SLOTSZ_SET(SAI_SLOT_SIZE_AUTO)) 8718c2ecf20Sopenharmony_ci sai->slot_width = sai->data_size; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci if (sai->slot_width < sai->data_size) { 8748c2ecf20Sopenharmony_ci dev_err(cpu_dai->dev, 8758c2ecf20Sopenharmony_ci "Data size %d larger than slot width\n", 8768c2ecf20Sopenharmony_ci sai->data_size); 8778c2ecf20Sopenharmony_ci return -EINVAL; 8788c2ecf20Sopenharmony_ci } 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci /* Slot number is set to 2, if not specified in DT */ 8818c2ecf20Sopenharmony_ci if (!sai->slots) 8828c2ecf20Sopenharmony_ci sai->slots = 2; 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci /* The number of slots in the audio frame is equal to NBSLOT[3:0] + 1*/ 8858c2ecf20Sopenharmony_ci stm32_sai_sub_reg_up(sai, STM_SAI_SLOTR_REGX, 8868c2ecf20Sopenharmony_ci SAI_XSLOTR_NBSLOT_MASK, 8878c2ecf20Sopenharmony_ci SAI_XSLOTR_NBSLOT_SET((sai->slots - 1))); 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci /* Set default slots mask if not already set from DT */ 8908c2ecf20Sopenharmony_ci if (!(slotr & SAI_XSLOTR_SLOTEN_MASK)) { 8918c2ecf20Sopenharmony_ci sai->slot_mask = (1 << sai->slots) - 1; 8928c2ecf20Sopenharmony_ci stm32_sai_sub_reg_up(sai, 8938c2ecf20Sopenharmony_ci STM_SAI_SLOTR_REGX, SAI_XSLOTR_SLOTEN_MASK, 8948c2ecf20Sopenharmony_ci SAI_XSLOTR_SLOTEN_SET(sai->slot_mask)); 8958c2ecf20Sopenharmony_ci } 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci dev_dbg(cpu_dai->dev, "Slots %d, slot width %d\n", 8988c2ecf20Sopenharmony_ci sai->slots, sai->slot_width); 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci return 0; 9018c2ecf20Sopenharmony_ci} 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_cistatic void stm32_sai_set_frame(struct snd_soc_dai *cpu_dai) 9048c2ecf20Sopenharmony_ci{ 9058c2ecf20Sopenharmony_ci struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); 9068c2ecf20Sopenharmony_ci int fs_active, offset, format; 9078c2ecf20Sopenharmony_ci int frcr, frcr_mask; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci format = sai->fmt & SND_SOC_DAIFMT_FORMAT_MASK; 9108c2ecf20Sopenharmony_ci sai->fs_length = sai->slot_width * sai->slots; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci fs_active = sai->fs_length / 2; 9138c2ecf20Sopenharmony_ci if ((format == SND_SOC_DAIFMT_DSP_A) || 9148c2ecf20Sopenharmony_ci (format == SND_SOC_DAIFMT_DSP_B)) 9158c2ecf20Sopenharmony_ci fs_active = 1; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci frcr = SAI_XFRCR_FRL_SET((sai->fs_length - 1)); 9188c2ecf20Sopenharmony_ci frcr |= SAI_XFRCR_FSALL_SET((fs_active - 1)); 9198c2ecf20Sopenharmony_ci frcr_mask = SAI_XFRCR_FRL_MASK | SAI_XFRCR_FSALL_MASK; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci dev_dbg(cpu_dai->dev, "Frame length %d, frame active %d\n", 9228c2ecf20Sopenharmony_ci sai->fs_length, fs_active); 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci stm32_sai_sub_reg_up(sai, STM_SAI_FRCR_REGX, frcr_mask, frcr); 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci if ((sai->fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_LSB) { 9278c2ecf20Sopenharmony_ci offset = sai->slot_width - sai->data_size; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci stm32_sai_sub_reg_up(sai, STM_SAI_SLOTR_REGX, 9308c2ecf20Sopenharmony_ci SAI_XSLOTR_FBOFF_MASK, 9318c2ecf20Sopenharmony_ci SAI_XSLOTR_FBOFF_SET(offset)); 9328c2ecf20Sopenharmony_ci } 9338c2ecf20Sopenharmony_ci} 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_cistatic void stm32_sai_init_iec958_status(struct stm32_sai_sub_data *sai) 9368c2ecf20Sopenharmony_ci{ 9378c2ecf20Sopenharmony_ci unsigned char *cs = sai->iec958.status; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci cs[0] = IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_NONE; 9408c2ecf20Sopenharmony_ci cs[1] = IEC958_AES1_CON_GENERAL; 9418c2ecf20Sopenharmony_ci cs[2] = IEC958_AES2_CON_SOURCE_UNSPEC | IEC958_AES2_CON_CHANNEL_UNSPEC; 9428c2ecf20Sopenharmony_ci cs[3] = IEC958_AES3_CON_CLOCK_1000PPM | IEC958_AES3_CON_FS_NOTID; 9438c2ecf20Sopenharmony_ci} 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_cistatic void stm32_sai_set_iec958_status(struct stm32_sai_sub_data *sai, 9468c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime) 9478c2ecf20Sopenharmony_ci{ 9488c2ecf20Sopenharmony_ci if (!runtime) 9498c2ecf20Sopenharmony_ci return; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci /* Force the sample rate according to runtime rate */ 9528c2ecf20Sopenharmony_ci mutex_lock(&sai->ctrl_lock); 9538c2ecf20Sopenharmony_ci switch (runtime->rate) { 9548c2ecf20Sopenharmony_ci case 22050: 9558c2ecf20Sopenharmony_ci sai->iec958.status[3] = IEC958_AES3_CON_FS_22050; 9568c2ecf20Sopenharmony_ci break; 9578c2ecf20Sopenharmony_ci case 44100: 9588c2ecf20Sopenharmony_ci sai->iec958.status[3] = IEC958_AES3_CON_FS_44100; 9598c2ecf20Sopenharmony_ci break; 9608c2ecf20Sopenharmony_ci case 88200: 9618c2ecf20Sopenharmony_ci sai->iec958.status[3] = IEC958_AES3_CON_FS_88200; 9628c2ecf20Sopenharmony_ci break; 9638c2ecf20Sopenharmony_ci case 176400: 9648c2ecf20Sopenharmony_ci sai->iec958.status[3] = IEC958_AES3_CON_FS_176400; 9658c2ecf20Sopenharmony_ci break; 9668c2ecf20Sopenharmony_ci case 24000: 9678c2ecf20Sopenharmony_ci sai->iec958.status[3] = IEC958_AES3_CON_FS_24000; 9688c2ecf20Sopenharmony_ci break; 9698c2ecf20Sopenharmony_ci case 48000: 9708c2ecf20Sopenharmony_ci sai->iec958.status[3] = IEC958_AES3_CON_FS_48000; 9718c2ecf20Sopenharmony_ci break; 9728c2ecf20Sopenharmony_ci case 96000: 9738c2ecf20Sopenharmony_ci sai->iec958.status[3] = IEC958_AES3_CON_FS_96000; 9748c2ecf20Sopenharmony_ci break; 9758c2ecf20Sopenharmony_ci case 192000: 9768c2ecf20Sopenharmony_ci sai->iec958.status[3] = IEC958_AES3_CON_FS_192000; 9778c2ecf20Sopenharmony_ci break; 9788c2ecf20Sopenharmony_ci case 32000: 9798c2ecf20Sopenharmony_ci sai->iec958.status[3] = IEC958_AES3_CON_FS_32000; 9808c2ecf20Sopenharmony_ci break; 9818c2ecf20Sopenharmony_ci default: 9828c2ecf20Sopenharmony_ci sai->iec958.status[3] = IEC958_AES3_CON_FS_NOTID; 9838c2ecf20Sopenharmony_ci break; 9848c2ecf20Sopenharmony_ci } 9858c2ecf20Sopenharmony_ci mutex_unlock(&sai->ctrl_lock); 9868c2ecf20Sopenharmony_ci} 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_cistatic int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai, 9898c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params) 9908c2ecf20Sopenharmony_ci{ 9918c2ecf20Sopenharmony_ci struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); 9928c2ecf20Sopenharmony_ci int div = 0, cr1 = 0; 9938c2ecf20Sopenharmony_ci int sai_clk_rate, mclk_ratio, den; 9948c2ecf20Sopenharmony_ci unsigned int rate = params_rate(params); 9958c2ecf20Sopenharmony_ci int ret; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci if (!sai->sai_mclk) { 9988c2ecf20Sopenharmony_ci ret = stm32_sai_set_parent_clock(sai, rate); 9998c2ecf20Sopenharmony_ci if (ret) 10008c2ecf20Sopenharmony_ci return ret; 10018c2ecf20Sopenharmony_ci } 10028c2ecf20Sopenharmony_ci sai_clk_rate = clk_get_rate(sai->sai_ck); 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci if (STM_SAI_IS_F4(sai->pdata)) { 10058c2ecf20Sopenharmony_ci /* mclk on (NODIV=0) 10068c2ecf20Sopenharmony_ci * mclk_rate = 256 * fs 10078c2ecf20Sopenharmony_ci * MCKDIV = 0 if sai_ck < 3/2 * mclk_rate 10088c2ecf20Sopenharmony_ci * MCKDIV = sai_ck / (2 * mclk_rate) otherwise 10098c2ecf20Sopenharmony_ci * mclk off (NODIV=1) 10108c2ecf20Sopenharmony_ci * MCKDIV ignored. sck = sai_ck 10118c2ecf20Sopenharmony_ci */ 10128c2ecf20Sopenharmony_ci if (!sai->mclk_rate) 10138c2ecf20Sopenharmony_ci return 0; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci if (2 * sai_clk_rate >= 3 * sai->mclk_rate) { 10168c2ecf20Sopenharmony_ci div = stm32_sai_get_clk_div(sai, sai_clk_rate, 10178c2ecf20Sopenharmony_ci 2 * sai->mclk_rate); 10188c2ecf20Sopenharmony_ci if (div < 0) 10198c2ecf20Sopenharmony_ci return div; 10208c2ecf20Sopenharmony_ci } 10218c2ecf20Sopenharmony_ci } else { 10228c2ecf20Sopenharmony_ci /* 10238c2ecf20Sopenharmony_ci * TDM mode : 10248c2ecf20Sopenharmony_ci * mclk on 10258c2ecf20Sopenharmony_ci * MCKDIV = sai_ck / (ws x 256) (NOMCK=0. OSR=0) 10268c2ecf20Sopenharmony_ci * MCKDIV = sai_ck / (ws x 512) (NOMCK=0. OSR=1) 10278c2ecf20Sopenharmony_ci * mclk off 10288c2ecf20Sopenharmony_ci * MCKDIV = sai_ck / (frl x ws) (NOMCK=1) 10298c2ecf20Sopenharmony_ci * Note: NOMCK/NODIV correspond to same bit. 10308c2ecf20Sopenharmony_ci */ 10318c2ecf20Sopenharmony_ci if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) { 10328c2ecf20Sopenharmony_ci div = stm32_sai_get_clk_div(sai, sai_clk_rate, 10338c2ecf20Sopenharmony_ci rate * 128); 10348c2ecf20Sopenharmony_ci if (div < 0) 10358c2ecf20Sopenharmony_ci return div; 10368c2ecf20Sopenharmony_ci } else { 10378c2ecf20Sopenharmony_ci if (sai->mclk_rate) { 10388c2ecf20Sopenharmony_ci mclk_ratio = sai->mclk_rate / rate; 10398c2ecf20Sopenharmony_ci if (mclk_ratio == 512) { 10408c2ecf20Sopenharmony_ci cr1 = SAI_XCR1_OSR; 10418c2ecf20Sopenharmony_ci } else if (mclk_ratio != 256) { 10428c2ecf20Sopenharmony_ci dev_err(cpu_dai->dev, 10438c2ecf20Sopenharmony_ci "Wrong mclk ratio %d\n", 10448c2ecf20Sopenharmony_ci mclk_ratio); 10458c2ecf20Sopenharmony_ci return -EINVAL; 10468c2ecf20Sopenharmony_ci } 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci stm32_sai_sub_reg_up(sai, 10498c2ecf20Sopenharmony_ci STM_SAI_CR1_REGX, 10508c2ecf20Sopenharmony_ci SAI_XCR1_OSR, cr1); 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci div = stm32_sai_get_clk_div(sai, sai_clk_rate, 10538c2ecf20Sopenharmony_ci sai->mclk_rate); 10548c2ecf20Sopenharmony_ci if (div < 0) 10558c2ecf20Sopenharmony_ci return div; 10568c2ecf20Sopenharmony_ci } else { 10578c2ecf20Sopenharmony_ci /* mclk-fs not set, master clock not active */ 10588c2ecf20Sopenharmony_ci den = sai->fs_length * params_rate(params); 10598c2ecf20Sopenharmony_ci div = stm32_sai_get_clk_div(sai, sai_clk_rate, 10608c2ecf20Sopenharmony_ci den); 10618c2ecf20Sopenharmony_ci if (div < 0) 10628c2ecf20Sopenharmony_ci return div; 10638c2ecf20Sopenharmony_ci } 10648c2ecf20Sopenharmony_ci } 10658c2ecf20Sopenharmony_ci } 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci return stm32_sai_set_clk_div(sai, div); 10688c2ecf20Sopenharmony_ci} 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_cistatic int stm32_sai_hw_params(struct snd_pcm_substream *substream, 10718c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 10728c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai) 10738c2ecf20Sopenharmony_ci{ 10748c2ecf20Sopenharmony_ci struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); 10758c2ecf20Sopenharmony_ci int ret; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci sai->data_size = params_width(params); 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) { 10808c2ecf20Sopenharmony_ci /* Rate not already set in runtime structure */ 10818c2ecf20Sopenharmony_ci substream->runtime->rate = params_rate(params); 10828c2ecf20Sopenharmony_ci stm32_sai_set_iec958_status(sai, substream->runtime); 10838c2ecf20Sopenharmony_ci } else { 10848c2ecf20Sopenharmony_ci ret = stm32_sai_set_slots(cpu_dai); 10858c2ecf20Sopenharmony_ci if (ret < 0) 10868c2ecf20Sopenharmony_ci return ret; 10878c2ecf20Sopenharmony_ci stm32_sai_set_frame(cpu_dai); 10888c2ecf20Sopenharmony_ci } 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci ret = stm32_sai_set_config(cpu_dai, substream, params); 10918c2ecf20Sopenharmony_ci if (ret) 10928c2ecf20Sopenharmony_ci return ret; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci if (sai->master) 10958c2ecf20Sopenharmony_ci ret = stm32_sai_configure_clock(cpu_dai, params); 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci return ret; 10988c2ecf20Sopenharmony_ci} 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_cistatic int stm32_sai_trigger(struct snd_pcm_substream *substream, int cmd, 11018c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai) 11028c2ecf20Sopenharmony_ci{ 11038c2ecf20Sopenharmony_ci struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); 11048c2ecf20Sopenharmony_ci int ret; 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci switch (cmd) { 11078c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 11088c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 11098c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 11108c2ecf20Sopenharmony_ci dev_dbg(cpu_dai->dev, "Enable DMA and SAI\n"); 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX, 11138c2ecf20Sopenharmony_ci SAI_XCR1_DMAEN, SAI_XCR1_DMAEN); 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci /* Enable SAI */ 11168c2ecf20Sopenharmony_ci ret = stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX, 11178c2ecf20Sopenharmony_ci SAI_XCR1_SAIEN, SAI_XCR1_SAIEN); 11188c2ecf20Sopenharmony_ci if (ret < 0) 11198c2ecf20Sopenharmony_ci dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); 11208c2ecf20Sopenharmony_ci break; 11218c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 11228c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 11238c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 11248c2ecf20Sopenharmony_ci dev_dbg(cpu_dai->dev, "Disable DMA and SAI\n"); 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci stm32_sai_sub_reg_up(sai, STM_SAI_IMR_REGX, 11278c2ecf20Sopenharmony_ci SAI_XIMR_MASK, 0); 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX, 11308c2ecf20Sopenharmony_ci SAI_XCR1_SAIEN, 11318c2ecf20Sopenharmony_ci (unsigned int)~SAI_XCR1_SAIEN); 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci ret = stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX, 11348c2ecf20Sopenharmony_ci SAI_XCR1_DMAEN, 11358c2ecf20Sopenharmony_ci (unsigned int)~SAI_XCR1_DMAEN); 11368c2ecf20Sopenharmony_ci if (ret < 0) 11378c2ecf20Sopenharmony_ci dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) 11408c2ecf20Sopenharmony_ci sai->spdif_frm_cnt = 0; 11418c2ecf20Sopenharmony_ci break; 11428c2ecf20Sopenharmony_ci default: 11438c2ecf20Sopenharmony_ci return -EINVAL; 11448c2ecf20Sopenharmony_ci } 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci return ret; 11478c2ecf20Sopenharmony_ci} 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_cistatic void stm32_sai_shutdown(struct snd_pcm_substream *substream, 11508c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai) 11518c2ecf20Sopenharmony_ci{ 11528c2ecf20Sopenharmony_ci struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); 11538c2ecf20Sopenharmony_ci unsigned long flags; 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci stm32_sai_sub_reg_up(sai, STM_SAI_IMR_REGX, SAI_XIMR_MASK, 0); 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci clk_disable_unprepare(sai->sai_ck); 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci spin_lock_irqsave(&sai->irq_lock, flags); 11608c2ecf20Sopenharmony_ci sai->substream = NULL; 11618c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sai->irq_lock, flags); 11628c2ecf20Sopenharmony_ci} 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_cistatic int stm32_sai_pcm_new(struct snd_soc_pcm_runtime *rtd, 11658c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai) 11668c2ecf20Sopenharmony_ci{ 11678c2ecf20Sopenharmony_ci struct stm32_sai_sub_data *sai = dev_get_drvdata(cpu_dai->dev); 11688c2ecf20Sopenharmony_ci struct snd_kcontrol_new knew = iec958_ctls; 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) { 11718c2ecf20Sopenharmony_ci dev_dbg(&sai->pdev->dev, "%s: register iec controls", __func__); 11728c2ecf20Sopenharmony_ci knew.device = rtd->pcm->device; 11738c2ecf20Sopenharmony_ci return snd_ctl_add(rtd->pcm->card, snd_ctl_new1(&knew, sai)); 11748c2ecf20Sopenharmony_ci } 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci return 0; 11778c2ecf20Sopenharmony_ci} 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_cistatic int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) 11808c2ecf20Sopenharmony_ci{ 11818c2ecf20Sopenharmony_ci struct stm32_sai_sub_data *sai = dev_get_drvdata(cpu_dai->dev); 11828c2ecf20Sopenharmony_ci int cr1 = 0, cr1_mask, ret; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci sai->cpu_dai = cpu_dai; 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci sai->dma_params.addr = (dma_addr_t)(sai->phys_addr + STM_SAI_DR_REGX); 11878c2ecf20Sopenharmony_ci /* 11888c2ecf20Sopenharmony_ci * DMA supports 4, 8 or 16 burst sizes. Burst size 4 is the best choice, 11898c2ecf20Sopenharmony_ci * as it allows bytes, half-word and words transfers. (See DMA fifos 11908c2ecf20Sopenharmony_ci * constraints). 11918c2ecf20Sopenharmony_ci */ 11928c2ecf20Sopenharmony_ci sai->dma_params.maxburst = 4; 11938c2ecf20Sopenharmony_ci if (sai->pdata->conf.fifo_size < 8) 11948c2ecf20Sopenharmony_ci sai->dma_params.maxburst = 1; 11958c2ecf20Sopenharmony_ci /* Buswidth will be set by framework at runtime */ 11968c2ecf20Sopenharmony_ci sai->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED; 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci if (STM_SAI_IS_PLAYBACK(sai)) 11998c2ecf20Sopenharmony_ci snd_soc_dai_init_dma_data(cpu_dai, &sai->dma_params, NULL); 12008c2ecf20Sopenharmony_ci else 12018c2ecf20Sopenharmony_ci snd_soc_dai_init_dma_data(cpu_dai, NULL, &sai->dma_params); 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci /* Next settings are not relevant for spdif mode */ 12048c2ecf20Sopenharmony_ci if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) 12058c2ecf20Sopenharmony_ci return 0; 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci cr1_mask = SAI_XCR1_RX_TX; 12088c2ecf20Sopenharmony_ci if (STM_SAI_IS_CAPTURE(sai)) 12098c2ecf20Sopenharmony_ci cr1 |= SAI_XCR1_RX_TX; 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci /* Configure synchronization */ 12128c2ecf20Sopenharmony_ci if (sai->sync == SAI_SYNC_EXTERNAL) { 12138c2ecf20Sopenharmony_ci /* Configure synchro client and provider */ 12148c2ecf20Sopenharmony_ci ret = sai->pdata->set_sync(sai->pdata, sai->np_sync_provider, 12158c2ecf20Sopenharmony_ci sai->synco, sai->synci); 12168c2ecf20Sopenharmony_ci if (ret) 12178c2ecf20Sopenharmony_ci return ret; 12188c2ecf20Sopenharmony_ci } 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci cr1_mask |= SAI_XCR1_SYNCEN_MASK; 12218c2ecf20Sopenharmony_ci cr1 |= SAI_XCR1_SYNCEN_SET(sai->sync); 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci return stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX, cr1_mask, cr1); 12248c2ecf20Sopenharmony_ci} 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops stm32_sai_pcm_dai_ops = { 12278c2ecf20Sopenharmony_ci .set_sysclk = stm32_sai_set_sysclk, 12288c2ecf20Sopenharmony_ci .set_fmt = stm32_sai_set_dai_fmt, 12298c2ecf20Sopenharmony_ci .set_tdm_slot = stm32_sai_set_dai_tdm_slot, 12308c2ecf20Sopenharmony_ci .startup = stm32_sai_startup, 12318c2ecf20Sopenharmony_ci .hw_params = stm32_sai_hw_params, 12328c2ecf20Sopenharmony_ci .trigger = stm32_sai_trigger, 12338c2ecf20Sopenharmony_ci .shutdown = stm32_sai_shutdown, 12348c2ecf20Sopenharmony_ci}; 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_cistatic int stm32_sai_pcm_process_spdif(struct snd_pcm_substream *substream, 12378c2ecf20Sopenharmony_ci int channel, unsigned long hwoff, 12388c2ecf20Sopenharmony_ci void *buf, unsigned long bytes) 12398c2ecf20Sopenharmony_ci{ 12408c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 12418c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 12428c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); 12438c2ecf20Sopenharmony_ci struct stm32_sai_sub_data *sai = dev_get_drvdata(cpu_dai->dev); 12448c2ecf20Sopenharmony_ci int *ptr = (int *)(runtime->dma_area + hwoff + 12458c2ecf20Sopenharmony_ci channel * (runtime->dma_bytes / runtime->channels)); 12468c2ecf20Sopenharmony_ci ssize_t cnt = bytes_to_samples(runtime, bytes); 12478c2ecf20Sopenharmony_ci unsigned int frm_cnt = sai->spdif_frm_cnt; 12488c2ecf20Sopenharmony_ci unsigned int byte; 12498c2ecf20Sopenharmony_ci unsigned int mask; 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci do { 12528c2ecf20Sopenharmony_ci *ptr = ((*ptr >> 8) & 0x00ffffff); 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci /* Set channel status bit */ 12558c2ecf20Sopenharmony_ci byte = frm_cnt >> 3; 12568c2ecf20Sopenharmony_ci mask = 1 << (frm_cnt - (byte << 3)); 12578c2ecf20Sopenharmony_ci if (sai->iec958.status[byte] & mask) 12588c2ecf20Sopenharmony_ci *ptr |= 0x04000000; 12598c2ecf20Sopenharmony_ci ptr++; 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci if (!(cnt % 2)) 12628c2ecf20Sopenharmony_ci frm_cnt++; 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci if (frm_cnt == SAI_IEC60958_BLOCK_FRAMES) 12658c2ecf20Sopenharmony_ci frm_cnt = 0; 12668c2ecf20Sopenharmony_ci } while (--cnt); 12678c2ecf20Sopenharmony_ci sai->spdif_frm_cnt = frm_cnt; 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci return 0; 12708c2ecf20Sopenharmony_ci} 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci/* No support of mmap in S/PDIF mode */ 12738c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware stm32_sai_pcm_hw_spdif = { 12748c2ecf20Sopenharmony_ci .info = SNDRV_PCM_INFO_INTERLEAVED, 12758c2ecf20Sopenharmony_ci .buffer_bytes_max = 8 * PAGE_SIZE, 12768c2ecf20Sopenharmony_ci .period_bytes_min = 1024, 12778c2ecf20Sopenharmony_ci .period_bytes_max = PAGE_SIZE, 12788c2ecf20Sopenharmony_ci .periods_min = 2, 12798c2ecf20Sopenharmony_ci .periods_max = 8, 12808c2ecf20Sopenharmony_ci}; 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware stm32_sai_pcm_hw = { 12838c2ecf20Sopenharmony_ci .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP, 12848c2ecf20Sopenharmony_ci .buffer_bytes_max = 8 * PAGE_SIZE, 12858c2ecf20Sopenharmony_ci .period_bytes_min = 1024, /* 5ms at 48kHz */ 12868c2ecf20Sopenharmony_ci .period_bytes_max = PAGE_SIZE, 12878c2ecf20Sopenharmony_ci .periods_min = 2, 12888c2ecf20Sopenharmony_ci .periods_max = 8, 12898c2ecf20Sopenharmony_ci}; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver stm32_sai_playback_dai = { 12928c2ecf20Sopenharmony_ci .probe = stm32_sai_dai_probe, 12938c2ecf20Sopenharmony_ci .pcm_new = stm32_sai_pcm_new, 12948c2ecf20Sopenharmony_ci .id = 1, /* avoid call to fmt_single_name() */ 12958c2ecf20Sopenharmony_ci .playback = { 12968c2ecf20Sopenharmony_ci .channels_min = 1, 12978c2ecf20Sopenharmony_ci .channels_max = 2, 12988c2ecf20Sopenharmony_ci .rate_min = 8000, 12998c2ecf20Sopenharmony_ci .rate_max = 192000, 13008c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS, 13018c2ecf20Sopenharmony_ci /* DMA does not support 24 bits transfers */ 13028c2ecf20Sopenharmony_ci .formats = 13038c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S8 | 13048c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_LE | 13058c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, 13068c2ecf20Sopenharmony_ci }, 13078c2ecf20Sopenharmony_ci .ops = &stm32_sai_pcm_dai_ops, 13088c2ecf20Sopenharmony_ci}; 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver stm32_sai_capture_dai = { 13118c2ecf20Sopenharmony_ci .probe = stm32_sai_dai_probe, 13128c2ecf20Sopenharmony_ci .id = 1, /* avoid call to fmt_single_name() */ 13138c2ecf20Sopenharmony_ci .capture = { 13148c2ecf20Sopenharmony_ci .channels_min = 1, 13158c2ecf20Sopenharmony_ci .channels_max = 2, 13168c2ecf20Sopenharmony_ci .rate_min = 8000, 13178c2ecf20Sopenharmony_ci .rate_max = 192000, 13188c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS, 13198c2ecf20Sopenharmony_ci /* DMA does not support 24 bits transfers */ 13208c2ecf20Sopenharmony_ci .formats = 13218c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S8 | 13228c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_LE | 13238c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, 13248c2ecf20Sopenharmony_ci }, 13258c2ecf20Sopenharmony_ci .ops = &stm32_sai_pcm_dai_ops, 13268c2ecf20Sopenharmony_ci}; 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_cistatic const struct snd_dmaengine_pcm_config stm32_sai_pcm_config = { 13298c2ecf20Sopenharmony_ci .pcm_hardware = &stm32_sai_pcm_hw, 13308c2ecf20Sopenharmony_ci .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, 13318c2ecf20Sopenharmony_ci}; 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_cistatic const struct snd_dmaengine_pcm_config stm32_sai_pcm_config_spdif = { 13348c2ecf20Sopenharmony_ci .pcm_hardware = &stm32_sai_pcm_hw_spdif, 13358c2ecf20Sopenharmony_ci .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, 13368c2ecf20Sopenharmony_ci .process = stm32_sai_pcm_process_spdif, 13378c2ecf20Sopenharmony_ci}; 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver stm32_component = { 13408c2ecf20Sopenharmony_ci .name = "stm32-sai", 13418c2ecf20Sopenharmony_ci}; 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_cistatic const struct of_device_id stm32_sai_sub_ids[] = { 13448c2ecf20Sopenharmony_ci { .compatible = "st,stm32-sai-sub-a", 13458c2ecf20Sopenharmony_ci .data = (void *)STM_SAI_A_ID}, 13468c2ecf20Sopenharmony_ci { .compatible = "st,stm32-sai-sub-b", 13478c2ecf20Sopenharmony_ci .data = (void *)STM_SAI_B_ID}, 13488c2ecf20Sopenharmony_ci {} 13498c2ecf20Sopenharmony_ci}; 13508c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, stm32_sai_sub_ids); 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_cistatic int stm32_sai_sub_parse_of(struct platform_device *pdev, 13538c2ecf20Sopenharmony_ci struct stm32_sai_sub_data *sai) 13548c2ecf20Sopenharmony_ci{ 13558c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 13568c2ecf20Sopenharmony_ci struct resource *res; 13578c2ecf20Sopenharmony_ci void __iomem *base; 13588c2ecf20Sopenharmony_ci struct of_phandle_args args; 13598c2ecf20Sopenharmony_ci int ret; 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci if (!np) 13628c2ecf20Sopenharmony_ci return -ENODEV; 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 13658c2ecf20Sopenharmony_ci base = devm_ioremap_resource(&pdev->dev, res); 13668c2ecf20Sopenharmony_ci if (IS_ERR(base)) 13678c2ecf20Sopenharmony_ci return PTR_ERR(base); 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci sai->phys_addr = res->start; 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci sai->regmap_config = &stm32_sai_sub_regmap_config_f4; 13728c2ecf20Sopenharmony_ci /* Note: PDM registers not available for sub-block B */ 13738c2ecf20Sopenharmony_ci if (STM_SAI_HAS_PDM(sai) && STM_SAI_IS_SUB_A(sai)) 13748c2ecf20Sopenharmony_ci sai->regmap_config = &stm32_sai_sub_regmap_config_h7; 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci /* 13778c2ecf20Sopenharmony_ci * Do not manage peripheral clock through regmap framework as this 13788c2ecf20Sopenharmony_ci * can lead to circular locking issue with sai master clock provider. 13798c2ecf20Sopenharmony_ci * Manage peripheral clock directly in driver instead. 13808c2ecf20Sopenharmony_ci */ 13818c2ecf20Sopenharmony_ci sai->regmap = devm_regmap_init_mmio(&pdev->dev, base, 13828c2ecf20Sopenharmony_ci sai->regmap_config); 13838c2ecf20Sopenharmony_ci if (IS_ERR(sai->regmap)) { 13848c2ecf20Sopenharmony_ci if (PTR_ERR(sai->regmap) != -EPROBE_DEFER) 13858c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Regmap init error %ld\n", 13868c2ecf20Sopenharmony_ci PTR_ERR(sai->regmap)); 13878c2ecf20Sopenharmony_ci return PTR_ERR(sai->regmap); 13888c2ecf20Sopenharmony_ci } 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci /* Get direction property */ 13918c2ecf20Sopenharmony_ci if (of_property_match_string(np, "dma-names", "tx") >= 0) { 13928c2ecf20Sopenharmony_ci sai->dir = SNDRV_PCM_STREAM_PLAYBACK; 13938c2ecf20Sopenharmony_ci } else if (of_property_match_string(np, "dma-names", "rx") >= 0) { 13948c2ecf20Sopenharmony_ci sai->dir = SNDRV_PCM_STREAM_CAPTURE; 13958c2ecf20Sopenharmony_ci } else { 13968c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unsupported direction\n"); 13978c2ecf20Sopenharmony_ci return -EINVAL; 13988c2ecf20Sopenharmony_ci } 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci /* Get spdif iec60958 property */ 14018c2ecf20Sopenharmony_ci sai->spdif = false; 14028c2ecf20Sopenharmony_ci if (of_get_property(np, "st,iec60958", NULL)) { 14038c2ecf20Sopenharmony_ci if (!STM_SAI_HAS_SPDIF(sai) || 14048c2ecf20Sopenharmony_ci sai->dir == SNDRV_PCM_STREAM_CAPTURE) { 14058c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "S/PDIF IEC60958 not supported\n"); 14068c2ecf20Sopenharmony_ci return -EINVAL; 14078c2ecf20Sopenharmony_ci } 14088c2ecf20Sopenharmony_ci stm32_sai_init_iec958_status(sai); 14098c2ecf20Sopenharmony_ci sai->spdif = true; 14108c2ecf20Sopenharmony_ci sai->master = true; 14118c2ecf20Sopenharmony_ci } 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci /* Get synchronization property */ 14148c2ecf20Sopenharmony_ci args.np = NULL; 14158c2ecf20Sopenharmony_ci ret = of_parse_phandle_with_fixed_args(np, "st,sync", 1, 0, &args); 14168c2ecf20Sopenharmony_ci if (ret < 0 && ret != -ENOENT) { 14178c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to get st,sync property\n"); 14188c2ecf20Sopenharmony_ci return ret; 14198c2ecf20Sopenharmony_ci } 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci sai->sync = SAI_SYNC_NONE; 14228c2ecf20Sopenharmony_ci if (args.np) { 14238c2ecf20Sopenharmony_ci if (args.np == np) { 14248c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "%pOFn sync own reference\n", np); 14258c2ecf20Sopenharmony_ci of_node_put(args.np); 14268c2ecf20Sopenharmony_ci return -EINVAL; 14278c2ecf20Sopenharmony_ci } 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci sai->np_sync_provider = of_get_parent(args.np); 14308c2ecf20Sopenharmony_ci if (!sai->np_sync_provider) { 14318c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "%pOFn parent node not found\n", 14328c2ecf20Sopenharmony_ci np); 14338c2ecf20Sopenharmony_ci of_node_put(args.np); 14348c2ecf20Sopenharmony_ci return -ENODEV; 14358c2ecf20Sopenharmony_ci } 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci sai->sync = SAI_SYNC_INTERNAL; 14388c2ecf20Sopenharmony_ci if (sai->np_sync_provider != sai->pdata->pdev->dev.of_node) { 14398c2ecf20Sopenharmony_ci if (!STM_SAI_HAS_EXT_SYNC(sai)) { 14408c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 14418c2ecf20Sopenharmony_ci "External synchro not supported\n"); 14428c2ecf20Sopenharmony_ci of_node_put(args.np); 14438c2ecf20Sopenharmony_ci return -EINVAL; 14448c2ecf20Sopenharmony_ci } 14458c2ecf20Sopenharmony_ci sai->sync = SAI_SYNC_EXTERNAL; 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci sai->synci = args.args[0]; 14488c2ecf20Sopenharmony_ci if (sai->synci < 1 || 14498c2ecf20Sopenharmony_ci (sai->synci > (SAI_GCR_SYNCIN_MAX + 1))) { 14508c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Wrong SAI index\n"); 14518c2ecf20Sopenharmony_ci of_node_put(args.np); 14528c2ecf20Sopenharmony_ci return -EINVAL; 14538c2ecf20Sopenharmony_ci } 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_ci if (of_property_match_string(args.np, "compatible", 14568c2ecf20Sopenharmony_ci "st,stm32-sai-sub-a") >= 0) 14578c2ecf20Sopenharmony_ci sai->synco = STM_SAI_SYNC_OUT_A; 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci if (of_property_match_string(args.np, "compatible", 14608c2ecf20Sopenharmony_ci "st,stm32-sai-sub-b") >= 0) 14618c2ecf20Sopenharmony_ci sai->synco = STM_SAI_SYNC_OUT_B; 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci if (!sai->synco) { 14648c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unknown SAI sub-block\n"); 14658c2ecf20Sopenharmony_ci of_node_put(args.np); 14668c2ecf20Sopenharmony_ci return -EINVAL; 14678c2ecf20Sopenharmony_ci } 14688c2ecf20Sopenharmony_ci } 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "%s synchronized with %s\n", 14718c2ecf20Sopenharmony_ci pdev->name, args.np->full_name); 14728c2ecf20Sopenharmony_ci } 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_ci of_node_put(args.np); 14758c2ecf20Sopenharmony_ci sai->sai_ck = devm_clk_get(&pdev->dev, "sai_ck"); 14768c2ecf20Sopenharmony_ci if (IS_ERR(sai->sai_ck)) { 14778c2ecf20Sopenharmony_ci if (PTR_ERR(sai->sai_ck) != -EPROBE_DEFER) 14788c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Missing kernel clock sai_ck: %ld\n", 14798c2ecf20Sopenharmony_ci PTR_ERR(sai->sai_ck)); 14808c2ecf20Sopenharmony_ci return PTR_ERR(sai->sai_ck); 14818c2ecf20Sopenharmony_ci } 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_ci ret = clk_prepare(sai->pdata->pclk); 14848c2ecf20Sopenharmony_ci if (ret < 0) 14858c2ecf20Sopenharmony_ci return ret; 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci if (STM_SAI_IS_F4(sai->pdata)) 14888c2ecf20Sopenharmony_ci return 0; 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci /* Register mclk provider if requested */ 14918c2ecf20Sopenharmony_ci if (of_find_property(np, "#clock-cells", NULL)) { 14928c2ecf20Sopenharmony_ci ret = stm32_sai_add_mclk_provider(sai); 14938c2ecf20Sopenharmony_ci if (ret < 0) 14948c2ecf20Sopenharmony_ci return ret; 14958c2ecf20Sopenharmony_ci } else { 14968c2ecf20Sopenharmony_ci sai->sai_mclk = devm_clk_get(&pdev->dev, "MCLK"); 14978c2ecf20Sopenharmony_ci if (IS_ERR(sai->sai_mclk)) { 14988c2ecf20Sopenharmony_ci if (PTR_ERR(sai->sai_mclk) != -ENOENT) 14998c2ecf20Sopenharmony_ci return PTR_ERR(sai->sai_mclk); 15008c2ecf20Sopenharmony_ci sai->sai_mclk = NULL; 15018c2ecf20Sopenharmony_ci } 15028c2ecf20Sopenharmony_ci } 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci return 0; 15058c2ecf20Sopenharmony_ci} 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_cistatic int stm32_sai_sub_probe(struct platform_device *pdev) 15088c2ecf20Sopenharmony_ci{ 15098c2ecf20Sopenharmony_ci struct stm32_sai_sub_data *sai; 15108c2ecf20Sopenharmony_ci const struct of_device_id *of_id; 15118c2ecf20Sopenharmony_ci const struct snd_dmaengine_pcm_config *conf = &stm32_sai_pcm_config; 15128c2ecf20Sopenharmony_ci int ret; 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); 15158c2ecf20Sopenharmony_ci if (!sai) 15168c2ecf20Sopenharmony_ci return -ENOMEM; 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci of_id = of_match_device(stm32_sai_sub_ids, &pdev->dev); 15198c2ecf20Sopenharmony_ci if (!of_id) 15208c2ecf20Sopenharmony_ci return -EINVAL; 15218c2ecf20Sopenharmony_ci sai->id = (uintptr_t)of_id->data; 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci sai->pdev = pdev; 15248c2ecf20Sopenharmony_ci mutex_init(&sai->ctrl_lock); 15258c2ecf20Sopenharmony_ci spin_lock_init(&sai->irq_lock); 15268c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, sai); 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci sai->pdata = dev_get_drvdata(pdev->dev.parent); 15298c2ecf20Sopenharmony_ci if (!sai->pdata) { 15308c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Parent device data not available\n"); 15318c2ecf20Sopenharmony_ci return -EINVAL; 15328c2ecf20Sopenharmony_ci } 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci ret = stm32_sai_sub_parse_of(pdev, sai); 15358c2ecf20Sopenharmony_ci if (ret) 15368c2ecf20Sopenharmony_ci return ret; 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci if (STM_SAI_IS_PLAYBACK(sai)) 15398c2ecf20Sopenharmony_ci sai->cpu_dai_drv = stm32_sai_playback_dai; 15408c2ecf20Sopenharmony_ci else 15418c2ecf20Sopenharmony_ci sai->cpu_dai_drv = stm32_sai_capture_dai; 15428c2ecf20Sopenharmony_ci sai->cpu_dai_drv.name = dev_name(&pdev->dev); 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci ret = devm_request_irq(&pdev->dev, sai->pdata->irq, stm32_sai_isr, 15458c2ecf20Sopenharmony_ci IRQF_SHARED, dev_name(&pdev->dev), sai); 15468c2ecf20Sopenharmony_ci if (ret) { 15478c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "IRQ request returned %d\n", ret); 15488c2ecf20Sopenharmony_ci return ret; 15498c2ecf20Sopenharmony_ci } 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) 15528c2ecf20Sopenharmony_ci conf = &stm32_sai_pcm_config_spdif; 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci ret = snd_dmaengine_pcm_register(&pdev->dev, conf, 0); 15558c2ecf20Sopenharmony_ci if (ret) { 15568c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 15578c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Could not register pcm dma\n"); 15588c2ecf20Sopenharmony_ci return ret; 15598c2ecf20Sopenharmony_ci } 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci ret = snd_soc_register_component(&pdev->dev, &stm32_component, 15628c2ecf20Sopenharmony_ci &sai->cpu_dai_drv, 1); 15638c2ecf20Sopenharmony_ci if (ret) { 15648c2ecf20Sopenharmony_ci snd_dmaengine_pcm_unregister(&pdev->dev); 15658c2ecf20Sopenharmony_ci return ret; 15668c2ecf20Sopenharmony_ci } 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci return 0; 15718c2ecf20Sopenharmony_ci} 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_cistatic int stm32_sai_sub_remove(struct platform_device *pdev) 15748c2ecf20Sopenharmony_ci{ 15758c2ecf20Sopenharmony_ci struct stm32_sai_sub_data *sai = dev_get_drvdata(&pdev->dev); 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci clk_unprepare(sai->pdata->pclk); 15788c2ecf20Sopenharmony_ci snd_dmaengine_pcm_unregister(&pdev->dev); 15798c2ecf20Sopenharmony_ci snd_soc_unregister_component(&pdev->dev); 15808c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci return 0; 15838c2ecf20Sopenharmony_ci} 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 15868c2ecf20Sopenharmony_cistatic int stm32_sai_sub_suspend(struct device *dev) 15878c2ecf20Sopenharmony_ci{ 15888c2ecf20Sopenharmony_ci struct stm32_sai_sub_data *sai = dev_get_drvdata(dev); 15898c2ecf20Sopenharmony_ci int ret; 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci ret = clk_enable(sai->pdata->pclk); 15928c2ecf20Sopenharmony_ci if (ret < 0) 15938c2ecf20Sopenharmony_ci return ret; 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci regcache_cache_only(sai->regmap, true); 15968c2ecf20Sopenharmony_ci regcache_mark_dirty(sai->regmap); 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci clk_disable(sai->pdata->pclk); 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci return 0; 16018c2ecf20Sopenharmony_ci} 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_cistatic int stm32_sai_sub_resume(struct device *dev) 16048c2ecf20Sopenharmony_ci{ 16058c2ecf20Sopenharmony_ci struct stm32_sai_sub_data *sai = dev_get_drvdata(dev); 16068c2ecf20Sopenharmony_ci int ret; 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_ci ret = clk_enable(sai->pdata->pclk); 16098c2ecf20Sopenharmony_ci if (ret < 0) 16108c2ecf20Sopenharmony_ci return ret; 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci regcache_cache_only(sai->regmap, false); 16138c2ecf20Sopenharmony_ci ret = regcache_sync(sai->regmap); 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci clk_disable(sai->pdata->pclk); 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_ci return ret; 16188c2ecf20Sopenharmony_ci} 16198c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_cistatic const struct dev_pm_ops stm32_sai_sub_pm_ops = { 16228c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(stm32_sai_sub_suspend, stm32_sai_sub_resume) 16238c2ecf20Sopenharmony_ci}; 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_cistatic struct platform_driver stm32_sai_sub_driver = { 16268c2ecf20Sopenharmony_ci .driver = { 16278c2ecf20Sopenharmony_ci .name = "st,stm32-sai-sub", 16288c2ecf20Sopenharmony_ci .of_match_table = stm32_sai_sub_ids, 16298c2ecf20Sopenharmony_ci .pm = &stm32_sai_sub_pm_ops, 16308c2ecf20Sopenharmony_ci }, 16318c2ecf20Sopenharmony_ci .probe = stm32_sai_sub_probe, 16328c2ecf20Sopenharmony_ci .remove = stm32_sai_sub_remove, 16338c2ecf20Sopenharmony_ci}; 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_cimodule_platform_driver(stm32_sai_sub_driver); 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("STM32 Soc SAI sub-block Interface"); 16388c2ecf20Sopenharmony_ciMODULE_AUTHOR("Olivier Moysan <olivier.moysan@st.com>"); 16398c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:st,stm32-sai-sub"); 16408c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1641