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/bitfield.h> 108c2ecf20Sopenharmony_ci#include <linux/clk.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 148c2ecf20Sopenharmony_ci#include <linux/pinctrl/consumer.h> 158c2ecf20Sopenharmony_ci#include <linux/reset.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <sound/dmaengine_pcm.h> 188c2ecf20Sopenharmony_ci#include <sound/core.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "stm32_sai.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic const struct stm32_sai_conf stm32_sai_conf_f4 = { 238c2ecf20Sopenharmony_ci .version = STM_SAI_STM32F4, 248c2ecf20Sopenharmony_ci .fifo_size = 8, 258c2ecf20Sopenharmony_ci .has_spdif_pdm = false, 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* 298c2ecf20Sopenharmony_ci * Default settings for stm32 H7 socs and next. 308c2ecf20Sopenharmony_ci * These default settings will be overridden if the soc provides 318c2ecf20Sopenharmony_ci * support of hardware configuration registers. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_cistatic const struct stm32_sai_conf stm32_sai_conf_h7 = { 348c2ecf20Sopenharmony_ci .version = STM_SAI_STM32H7, 358c2ecf20Sopenharmony_ci .fifo_size = 8, 368c2ecf20Sopenharmony_ci .has_spdif_pdm = true, 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic const struct of_device_id stm32_sai_ids[] = { 408c2ecf20Sopenharmony_ci { .compatible = "st,stm32f4-sai", .data = (void *)&stm32_sai_conf_f4 }, 418c2ecf20Sopenharmony_ci { .compatible = "st,stm32h7-sai", .data = (void *)&stm32_sai_conf_h7 }, 428c2ecf20Sopenharmony_ci {} 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic int stm32_sai_pclk_disable(struct device *dev) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci struct stm32_sai_data *sai = dev_get_drvdata(dev); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci clk_disable_unprepare(sai->pclk); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci return 0; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic int stm32_sai_pclk_enable(struct device *dev) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci struct stm32_sai_data *sai = dev_get_drvdata(dev); 578c2ecf20Sopenharmony_ci int ret; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci ret = clk_prepare_enable(sai->pclk); 608c2ecf20Sopenharmony_ci if (ret) { 618c2ecf20Sopenharmony_ci dev_err(&sai->pdev->dev, "failed to enable clock: %d\n", ret); 628c2ecf20Sopenharmony_ci return ret; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci return 0; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic int stm32_sai_sync_conf_client(struct stm32_sai_data *sai, int synci) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci int ret; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci /* Enable peripheral clock to allow GCR register access */ 738c2ecf20Sopenharmony_ci ret = stm32_sai_pclk_enable(&sai->pdev->dev); 748c2ecf20Sopenharmony_ci if (ret) 758c2ecf20Sopenharmony_ci return ret; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci writel_relaxed(FIELD_PREP(SAI_GCR_SYNCIN_MASK, (synci - 1)), sai->base); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci stm32_sai_pclk_disable(&sai->pdev->dev); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci return 0; 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic int stm32_sai_sync_conf_provider(struct stm32_sai_data *sai, int synco) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci u32 prev_synco; 878c2ecf20Sopenharmony_ci int ret; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* Enable peripheral clock to allow GCR register access */ 908c2ecf20Sopenharmony_ci ret = stm32_sai_pclk_enable(&sai->pdev->dev); 918c2ecf20Sopenharmony_ci if (ret) 928c2ecf20Sopenharmony_ci return ret; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci dev_dbg(&sai->pdev->dev, "Set %pOFn%s as synchro provider\n", 958c2ecf20Sopenharmony_ci sai->pdev->dev.of_node, 968c2ecf20Sopenharmony_ci synco == STM_SAI_SYNC_OUT_A ? "A" : "B"); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci prev_synco = FIELD_GET(SAI_GCR_SYNCOUT_MASK, readl_relaxed(sai->base)); 998c2ecf20Sopenharmony_ci if (prev_synco != STM_SAI_SYNC_OUT_NONE && synco != prev_synco) { 1008c2ecf20Sopenharmony_ci dev_err(&sai->pdev->dev, "%pOFn%s already set as sync provider\n", 1018c2ecf20Sopenharmony_ci sai->pdev->dev.of_node, 1028c2ecf20Sopenharmony_ci prev_synco == STM_SAI_SYNC_OUT_A ? "A" : "B"); 1038c2ecf20Sopenharmony_ci stm32_sai_pclk_disable(&sai->pdev->dev); 1048c2ecf20Sopenharmony_ci return -EINVAL; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci writel_relaxed(FIELD_PREP(SAI_GCR_SYNCOUT_MASK, synco), sai->base); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci stm32_sai_pclk_disable(&sai->pdev->dev); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci return 0; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic int stm32_sai_set_sync(struct stm32_sai_data *sai_client, 1158c2ecf20Sopenharmony_ci struct device_node *np_provider, 1168c2ecf20Sopenharmony_ci int synco, int synci) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct platform_device *pdev = of_find_device_by_node(np_provider); 1198c2ecf20Sopenharmony_ci struct stm32_sai_data *sai_provider; 1208c2ecf20Sopenharmony_ci int ret; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (!pdev) { 1238c2ecf20Sopenharmony_ci dev_err(&sai_client->pdev->dev, 1248c2ecf20Sopenharmony_ci "Device not found for node %pOFn\n", np_provider); 1258c2ecf20Sopenharmony_ci of_node_put(np_provider); 1268c2ecf20Sopenharmony_ci return -ENODEV; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci sai_provider = platform_get_drvdata(pdev); 1308c2ecf20Sopenharmony_ci if (!sai_provider) { 1318c2ecf20Sopenharmony_ci dev_err(&sai_client->pdev->dev, 1328c2ecf20Sopenharmony_ci "SAI sync provider data not found\n"); 1338c2ecf20Sopenharmony_ci ret = -EINVAL; 1348c2ecf20Sopenharmony_ci goto error; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* Configure sync client */ 1388c2ecf20Sopenharmony_ci ret = stm32_sai_sync_conf_client(sai_client, synci); 1398c2ecf20Sopenharmony_ci if (ret < 0) 1408c2ecf20Sopenharmony_ci goto error; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* Configure sync provider */ 1438c2ecf20Sopenharmony_ci ret = stm32_sai_sync_conf_provider(sai_provider, synco); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cierror: 1468c2ecf20Sopenharmony_ci put_device(&pdev->dev); 1478c2ecf20Sopenharmony_ci of_node_put(np_provider); 1488c2ecf20Sopenharmony_ci return ret; 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic int stm32_sai_probe(struct platform_device *pdev) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci struct stm32_sai_data *sai; 1548c2ecf20Sopenharmony_ci struct reset_control *rst; 1558c2ecf20Sopenharmony_ci const struct of_device_id *of_id; 1568c2ecf20Sopenharmony_ci u32 val; 1578c2ecf20Sopenharmony_ci int ret; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); 1608c2ecf20Sopenharmony_ci if (!sai) 1618c2ecf20Sopenharmony_ci return -ENOMEM; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci sai->base = devm_platform_ioremap_resource(pdev, 0); 1648c2ecf20Sopenharmony_ci if (IS_ERR(sai->base)) 1658c2ecf20Sopenharmony_ci return PTR_ERR(sai->base); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci of_id = of_match_device(stm32_sai_ids, &pdev->dev); 1688c2ecf20Sopenharmony_ci if (of_id) 1698c2ecf20Sopenharmony_ci memcpy(&sai->conf, (const struct stm32_sai_conf *)of_id->data, 1708c2ecf20Sopenharmony_ci sizeof(struct stm32_sai_conf)); 1718c2ecf20Sopenharmony_ci else 1728c2ecf20Sopenharmony_ci return -EINVAL; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (!STM_SAI_IS_F4(sai)) { 1758c2ecf20Sopenharmony_ci sai->pclk = devm_clk_get(&pdev->dev, "pclk"); 1768c2ecf20Sopenharmony_ci if (IS_ERR(sai->pclk)) { 1778c2ecf20Sopenharmony_ci if (PTR_ERR(sai->pclk) != -EPROBE_DEFER) 1788c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "missing bus clock pclk: %ld\n", 1798c2ecf20Sopenharmony_ci PTR_ERR(sai->pclk)); 1808c2ecf20Sopenharmony_ci return PTR_ERR(sai->pclk); 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci sai->clk_x8k = devm_clk_get(&pdev->dev, "x8k"); 1858c2ecf20Sopenharmony_ci if (IS_ERR(sai->clk_x8k)) { 1868c2ecf20Sopenharmony_ci if (PTR_ERR(sai->clk_x8k) != -EPROBE_DEFER) 1878c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "missing x8k parent clock: %ld\n", 1888c2ecf20Sopenharmony_ci PTR_ERR(sai->clk_x8k)); 1898c2ecf20Sopenharmony_ci return PTR_ERR(sai->clk_x8k); 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci sai->clk_x11k = devm_clk_get(&pdev->dev, "x11k"); 1938c2ecf20Sopenharmony_ci if (IS_ERR(sai->clk_x11k)) { 1948c2ecf20Sopenharmony_ci if (PTR_ERR(sai->clk_x11k) != -EPROBE_DEFER) 1958c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "missing x11k parent clock: %ld\n", 1968c2ecf20Sopenharmony_ci PTR_ERR(sai->clk_x11k)); 1978c2ecf20Sopenharmony_ci return PTR_ERR(sai->clk_x11k); 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* init irqs */ 2018c2ecf20Sopenharmony_ci sai->irq = platform_get_irq(pdev, 0); 2028c2ecf20Sopenharmony_ci if (sai->irq < 0) 2038c2ecf20Sopenharmony_ci return sai->irq; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /* reset */ 2068c2ecf20Sopenharmony_ci rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); 2078c2ecf20Sopenharmony_ci if (IS_ERR(rst)) { 2088c2ecf20Sopenharmony_ci if (PTR_ERR(rst) != -EPROBE_DEFER) 2098c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Reset controller error %ld\n", 2108c2ecf20Sopenharmony_ci PTR_ERR(rst)); 2118c2ecf20Sopenharmony_ci return PTR_ERR(rst); 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci reset_control_assert(rst); 2148c2ecf20Sopenharmony_ci udelay(2); 2158c2ecf20Sopenharmony_ci reset_control_deassert(rst); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci /* Enable peripheral clock to allow register access */ 2188c2ecf20Sopenharmony_ci ret = clk_prepare_enable(sai->pclk); 2198c2ecf20Sopenharmony_ci if (ret) { 2208c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to enable clock: %d\n", ret); 2218c2ecf20Sopenharmony_ci return ret; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci val = FIELD_GET(SAI_IDR_ID_MASK, 2258c2ecf20Sopenharmony_ci readl_relaxed(sai->base + STM_SAI_IDR)); 2268c2ecf20Sopenharmony_ci if (val == SAI_IPIDR_NUMBER) { 2278c2ecf20Sopenharmony_ci val = readl_relaxed(sai->base + STM_SAI_HWCFGR); 2288c2ecf20Sopenharmony_ci sai->conf.fifo_size = FIELD_GET(SAI_HWCFGR_FIFO_SIZE, val); 2298c2ecf20Sopenharmony_ci sai->conf.has_spdif_pdm = !!FIELD_GET(SAI_HWCFGR_SPDIF_PDM, 2308c2ecf20Sopenharmony_ci val); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci val = readl_relaxed(sai->base + STM_SAI_VERR); 2338c2ecf20Sopenharmony_ci sai->conf.version = val; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "SAI version: %lu.%lu registered\n", 2368c2ecf20Sopenharmony_ci FIELD_GET(SAI_VERR_MAJ_MASK, val), 2378c2ecf20Sopenharmony_ci FIELD_GET(SAI_VERR_MIN_MASK, val)); 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci clk_disable_unprepare(sai->pclk); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci sai->pdev = pdev; 2428c2ecf20Sopenharmony_ci sai->set_sync = &stm32_sai_set_sync; 2438c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, sai); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci return devm_of_platform_populate(&pdev->dev); 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 2498c2ecf20Sopenharmony_ci/* 2508c2ecf20Sopenharmony_ci * When pins are shared by two sai sub instances, pins have to be defined 2518c2ecf20Sopenharmony_ci * in sai parent node. In this case, pins state is not managed by alsa fw. 2528c2ecf20Sopenharmony_ci * These pins are managed in suspend/resume callbacks. 2538c2ecf20Sopenharmony_ci */ 2548c2ecf20Sopenharmony_cistatic int stm32_sai_suspend(struct device *dev) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci struct stm32_sai_data *sai = dev_get_drvdata(dev); 2578c2ecf20Sopenharmony_ci int ret; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci ret = stm32_sai_pclk_enable(dev); 2608c2ecf20Sopenharmony_ci if (ret) 2618c2ecf20Sopenharmony_ci return ret; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci sai->gcr = readl_relaxed(sai->base); 2648c2ecf20Sopenharmony_ci stm32_sai_pclk_disable(dev); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci return pinctrl_pm_select_sleep_state(dev); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic int stm32_sai_resume(struct device *dev) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct stm32_sai_data *sai = dev_get_drvdata(dev); 2728c2ecf20Sopenharmony_ci int ret; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci ret = stm32_sai_pclk_enable(dev); 2758c2ecf20Sopenharmony_ci if (ret) 2768c2ecf20Sopenharmony_ci return ret; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci writel_relaxed(sai->gcr, sai->base); 2798c2ecf20Sopenharmony_ci stm32_sai_pclk_disable(dev); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci return pinctrl_pm_select_default_state(dev); 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic const struct dev_pm_ops stm32_sai_pm_ops = { 2868c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(stm32_sai_suspend, stm32_sai_resume) 2878c2ecf20Sopenharmony_ci}; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, stm32_sai_ids); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic struct platform_driver stm32_sai_driver = { 2928c2ecf20Sopenharmony_ci .driver = { 2938c2ecf20Sopenharmony_ci .name = "st,stm32-sai", 2948c2ecf20Sopenharmony_ci .of_match_table = stm32_sai_ids, 2958c2ecf20Sopenharmony_ci .pm = &stm32_sai_pm_ops, 2968c2ecf20Sopenharmony_ci }, 2978c2ecf20Sopenharmony_ci .probe = stm32_sai_probe, 2988c2ecf20Sopenharmony_ci}; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cimodule_platform_driver(stm32_sai_driver); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("STM32 Soc SAI Interface"); 3038c2ecf20Sopenharmony_ciMODULE_AUTHOR("Olivier Moysan <olivier.moysan@st.com>"); 3048c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:st,stm32-sai"); 3058c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 306