xref: /openbmc/linux/sound/soc/stm/stm32_sai.c (revision 1802d0be)
11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
23e086edfSolivier moysan /*
33e086edfSolivier moysan  * STM32 ALSA SoC Digital Audio Interface (SAI) driver.
43e086edfSolivier moysan  *
53e086edfSolivier moysan  * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
63e086edfSolivier moysan  * Author(s): Olivier Moysan <olivier.moysan@st.com> for STMicroelectronics.
73e086edfSolivier moysan  */
83e086edfSolivier moysan 
95914d285SOlivier Moysan #include <linux/bitfield.h>
103e086edfSolivier moysan #include <linux/clk.h>
113e086edfSolivier moysan #include <linux/delay.h>
123e086edfSolivier moysan #include <linux/module.h>
133e086edfSolivier moysan #include <linux/of_platform.h>
14cf881773SOlivier Moysan #include <linux/pinctrl/consumer.h>
153e086edfSolivier moysan #include <linux/reset.h>
163e086edfSolivier moysan 
173e086edfSolivier moysan #include <sound/dmaengine_pcm.h>
183e086edfSolivier moysan #include <sound/core.h>
193e086edfSolivier moysan 
203e086edfSolivier moysan #include "stm32_sai.h"
213e086edfSolivier moysan 
2203e78a24Solivier moysan static const struct stm32_sai_conf stm32_sai_conf_f4 = {
2303e78a24Solivier moysan 	.version = SAI_STM32F4,
246eb17d70SOlivier Moysan 	.has_spdif = false,
2503e78a24Solivier moysan };
2603e78a24Solivier moysan 
2703e78a24Solivier moysan static const struct stm32_sai_conf stm32_sai_conf_h7 = {
2803e78a24Solivier moysan 	.version = SAI_STM32H7,
296eb17d70SOlivier Moysan 	.has_spdif = true,
3003e78a24Solivier moysan };
3103e78a24Solivier moysan 
323e086edfSolivier moysan static const struct of_device_id stm32_sai_ids[] = {
3303e78a24Solivier moysan 	{ .compatible = "st,stm32f4-sai", .data = (void *)&stm32_sai_conf_f4 },
3403e78a24Solivier moysan 	{ .compatible = "st,stm32h7-sai", .data = (void *)&stm32_sai_conf_h7 },
353e086edfSolivier moysan 	{}
363e086edfSolivier moysan };
373e086edfSolivier moysan 
38cf881773SOlivier Moysan static int stm32_sai_pclk_disable(struct device *dev)
395914d285SOlivier Moysan {
40cf881773SOlivier Moysan 	struct stm32_sai_data *sai = dev_get_drvdata(dev);
41cf881773SOlivier Moysan 
42cf881773SOlivier Moysan 	clk_disable_unprepare(sai->pclk);
43cf881773SOlivier Moysan 
44cf881773SOlivier Moysan 	return 0;
45cf881773SOlivier Moysan }
46cf881773SOlivier Moysan 
47cf881773SOlivier Moysan static int stm32_sai_pclk_enable(struct device *dev)
48cf881773SOlivier Moysan {
49cf881773SOlivier Moysan 	struct stm32_sai_data *sai = dev_get_drvdata(dev);
505914d285SOlivier Moysan 	int ret;
515914d285SOlivier Moysan 
525914d285SOlivier Moysan 	ret = clk_prepare_enable(sai->pclk);
535914d285SOlivier Moysan 	if (ret) {
545914d285SOlivier Moysan 		dev_err(&sai->pdev->dev, "failed to enable clock: %d\n", ret);
555914d285SOlivier Moysan 		return ret;
565914d285SOlivier Moysan 	}
575914d285SOlivier Moysan 
58cf881773SOlivier Moysan 	return 0;
59cf881773SOlivier Moysan }
60cf881773SOlivier Moysan 
61cf881773SOlivier Moysan static int stm32_sai_sync_conf_client(struct stm32_sai_data *sai, int synci)
62cf881773SOlivier Moysan {
63cf881773SOlivier Moysan 	int ret;
64cf881773SOlivier Moysan 
65cf881773SOlivier Moysan 	/* Enable peripheral clock to allow GCR register access */
66cf881773SOlivier Moysan 	ret = stm32_sai_pclk_enable(&sai->pdev->dev);
67cf881773SOlivier Moysan 	if (ret)
68cf881773SOlivier Moysan 		return ret;
69cf881773SOlivier Moysan 
705914d285SOlivier Moysan 	writel_relaxed(FIELD_PREP(SAI_GCR_SYNCIN_MASK, (synci - 1)), sai->base);
715914d285SOlivier Moysan 
72cf881773SOlivier Moysan 	stm32_sai_pclk_disable(&sai->pdev->dev);
735914d285SOlivier Moysan 
745914d285SOlivier Moysan 	return 0;
755914d285SOlivier Moysan }
765914d285SOlivier Moysan 
777dd0d835Solivier moysan static int stm32_sai_sync_conf_provider(struct stm32_sai_data *sai, int synco)
785914d285SOlivier Moysan {
795914d285SOlivier Moysan 	u32 prev_synco;
805914d285SOlivier Moysan 	int ret;
815914d285SOlivier Moysan 
825914d285SOlivier Moysan 	/* Enable peripheral clock to allow GCR register access */
83cf881773SOlivier Moysan 	ret = stm32_sai_pclk_enable(&sai->pdev->dev);
84cf881773SOlivier Moysan 	if (ret)
855914d285SOlivier Moysan 		return ret;
865914d285SOlivier Moysan 
87dc43d3aaSRob Herring 	dev_dbg(&sai->pdev->dev, "Set %pOFn%s as synchro provider\n",
88dc43d3aaSRob Herring 		sai->pdev->dev.of_node,
895914d285SOlivier Moysan 		synco == STM_SAI_SYNC_OUT_A ? "A" : "B");
905914d285SOlivier Moysan 
915914d285SOlivier Moysan 	prev_synco = FIELD_GET(SAI_GCR_SYNCOUT_MASK, readl_relaxed(sai->base));
925914d285SOlivier Moysan 	if (prev_synco != STM_SAI_SYNC_OUT_NONE && synco != prev_synco) {
93dc43d3aaSRob Herring 		dev_err(&sai->pdev->dev, "%pOFn%s already set as sync provider\n",
94dc43d3aaSRob Herring 			sai->pdev->dev.of_node,
955914d285SOlivier Moysan 			prev_synco == STM_SAI_SYNC_OUT_A ? "A" : "B");
96cf881773SOlivier Moysan 			stm32_sai_pclk_disable(&sai->pdev->dev);
975914d285SOlivier Moysan 		return -EINVAL;
985914d285SOlivier Moysan 	}
995914d285SOlivier Moysan 
1005914d285SOlivier Moysan 	writel_relaxed(FIELD_PREP(SAI_GCR_SYNCOUT_MASK, synco), sai->base);
1015914d285SOlivier Moysan 
102cf881773SOlivier Moysan 	stm32_sai_pclk_disable(&sai->pdev->dev);
1035914d285SOlivier Moysan 
1045914d285SOlivier Moysan 	return 0;
1055914d285SOlivier Moysan }
1065914d285SOlivier Moysan 
1077dd0d835Solivier moysan static int stm32_sai_set_sync(struct stm32_sai_data *sai_client,
1085914d285SOlivier Moysan 			      struct device_node *np_provider,
1095914d285SOlivier Moysan 			      int synco, int synci)
1105914d285SOlivier Moysan {
1117dd0d835Solivier moysan 	struct platform_device *pdev = of_find_device_by_node(np_provider);
1127dd0d835Solivier moysan 	struct stm32_sai_data *sai_provider;
1135914d285SOlivier Moysan 	int ret;
1145914d285SOlivier Moysan 
1157dd0d835Solivier moysan 	if (!pdev) {
1167dd0d835Solivier moysan 		dev_err(&sai_client->pdev->dev,
1175d585e1eSRob Herring 			"Device not found for node %pOFn\n", np_provider);
118d4180b4cSOlivier Moysan 		of_node_put(np_provider);
1197dd0d835Solivier moysan 		return -ENODEV;
1207dd0d835Solivier moysan 	}
1217dd0d835Solivier moysan 
1227dd0d835Solivier moysan 	sai_provider = platform_get_drvdata(pdev);
1237dd0d835Solivier moysan 	if (!sai_provider) {
1247dd0d835Solivier moysan 		dev_err(&sai_client->pdev->dev,
1257dd0d835Solivier moysan 			"SAI sync provider data not found\n");
1261c3816a1SWen Yang 		ret = -EINVAL;
127d4180b4cSOlivier Moysan 		goto error;
1287dd0d835Solivier moysan 	}
1297dd0d835Solivier moysan 
1305914d285SOlivier Moysan 	/* Configure sync client */
1317dd0d835Solivier moysan 	ret = stm32_sai_sync_conf_client(sai_client, synci);
1327dd0d835Solivier moysan 	if (ret < 0)
133d4180b4cSOlivier Moysan 		goto error;
1345914d285SOlivier Moysan 
1355914d285SOlivier Moysan 	/* Configure sync provider */
1361c3816a1SWen Yang 	ret = stm32_sai_sync_conf_provider(sai_provider, synco);
1371c3816a1SWen Yang 
138d4180b4cSOlivier Moysan error:
1391c3816a1SWen Yang 	put_device(&pdev->dev);
140d4180b4cSOlivier Moysan 	of_node_put(np_provider);
1411c3816a1SWen Yang 	return ret;
1425914d285SOlivier Moysan }
1435914d285SOlivier Moysan 
1443e086edfSolivier moysan static int stm32_sai_probe(struct platform_device *pdev)
1453e086edfSolivier moysan {
1463e086edfSolivier moysan 	struct stm32_sai_data *sai;
1473e086edfSolivier moysan 	struct reset_control *rst;
1483e086edfSolivier moysan 	struct resource *res;
1493e086edfSolivier moysan 	const struct of_device_id *of_id;
1503e086edfSolivier moysan 
1513e086edfSolivier moysan 	sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
1523e086edfSolivier moysan 	if (!sai)
1533e086edfSolivier moysan 		return -ENOMEM;
1543e086edfSolivier moysan 
1553e086edfSolivier moysan 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1565914d285SOlivier Moysan 	sai->base = devm_ioremap_resource(&pdev->dev, res);
1575914d285SOlivier Moysan 	if (IS_ERR(sai->base))
1585914d285SOlivier Moysan 		return PTR_ERR(sai->base);
1593e086edfSolivier moysan 
1603e086edfSolivier moysan 	of_id = of_match_device(stm32_sai_ids, &pdev->dev);
1613e086edfSolivier moysan 	if (of_id)
16203e78a24Solivier moysan 		sai->conf = (struct stm32_sai_conf *)of_id->data;
1633e086edfSolivier moysan 	else
1643e086edfSolivier moysan 		return -EINVAL;
1653e086edfSolivier moysan 
1665914d285SOlivier Moysan 	if (!STM_SAI_IS_F4(sai)) {
1675914d285SOlivier Moysan 		sai->pclk = devm_clk_get(&pdev->dev, "pclk");
1685914d285SOlivier Moysan 		if (IS_ERR(sai->pclk)) {
1695914d285SOlivier Moysan 			dev_err(&pdev->dev, "missing bus clock pclk\n");
1705914d285SOlivier Moysan 			return PTR_ERR(sai->pclk);
1715914d285SOlivier Moysan 		}
1725914d285SOlivier Moysan 	}
1735914d285SOlivier Moysan 
1743e086edfSolivier moysan 	sai->clk_x8k = devm_clk_get(&pdev->dev, "x8k");
1753e086edfSolivier moysan 	if (IS_ERR(sai->clk_x8k)) {
1763e086edfSolivier moysan 		dev_err(&pdev->dev, "missing x8k parent clock\n");
1773e086edfSolivier moysan 		return PTR_ERR(sai->clk_x8k);
1783e086edfSolivier moysan 	}
1793e086edfSolivier moysan 
1803e086edfSolivier moysan 	sai->clk_x11k = devm_clk_get(&pdev->dev, "x11k");
1813e086edfSolivier moysan 	if (IS_ERR(sai->clk_x11k)) {
1823e086edfSolivier moysan 		dev_err(&pdev->dev, "missing x11k parent clock\n");
1833e086edfSolivier moysan 		return PTR_ERR(sai->clk_x11k);
1843e086edfSolivier moysan 	}
1853e086edfSolivier moysan 
1863e086edfSolivier moysan 	/* init irqs */
1873e086edfSolivier moysan 	sai->irq = platform_get_irq(pdev, 0);
1883e086edfSolivier moysan 	if (sai->irq < 0) {
1893e086edfSolivier moysan 		dev_err(&pdev->dev, "no irq for node %s\n", pdev->name);
1903e086edfSolivier moysan 		return sai->irq;
1913e086edfSolivier moysan 	}
1923e086edfSolivier moysan 
1933e086edfSolivier moysan 	/* reset */
1943c6f6c53SOlivier Moysan 	rst = devm_reset_control_get_exclusive(&pdev->dev, NULL);
1953e086edfSolivier moysan 	if (!IS_ERR(rst)) {
1963e086edfSolivier moysan 		reset_control_assert(rst);
1973e086edfSolivier moysan 		udelay(2);
1983e086edfSolivier moysan 		reset_control_deassert(rst);
1993e086edfSolivier moysan 	}
2003e086edfSolivier moysan 
2013e086edfSolivier moysan 	sai->pdev = pdev;
2027dd0d835Solivier moysan 	sai->set_sync = &stm32_sai_set_sync;
2033e086edfSolivier moysan 	platform_set_drvdata(pdev, sai);
2043e086edfSolivier moysan 
205512d1bb4Solivier moysan 	return devm_of_platform_populate(&pdev->dev);
2063e086edfSolivier moysan }
2073e086edfSolivier moysan 
208cf881773SOlivier Moysan #ifdef CONFIG_PM_SLEEP
209cf881773SOlivier Moysan /*
210cf881773SOlivier Moysan  * When pins are shared by two sai sub instances, pins have to be defined
211cf881773SOlivier Moysan  * in sai parent node. In this case, pins state is not managed by alsa fw.
212cf881773SOlivier Moysan  * These pins are managed in suspend/resume callbacks.
213cf881773SOlivier Moysan  */
214cf881773SOlivier Moysan static int stm32_sai_suspend(struct device *dev)
215cf881773SOlivier Moysan {
216cf881773SOlivier Moysan 	struct stm32_sai_data *sai = dev_get_drvdata(dev);
217cf881773SOlivier Moysan 	int ret;
218cf881773SOlivier Moysan 
219cf881773SOlivier Moysan 	ret = stm32_sai_pclk_enable(dev);
220cf881773SOlivier Moysan 	if (ret)
221cf881773SOlivier Moysan 		return ret;
222cf881773SOlivier Moysan 
223cf881773SOlivier Moysan 	sai->gcr = readl_relaxed(sai->base);
224cf881773SOlivier Moysan 	stm32_sai_pclk_disable(dev);
225cf881773SOlivier Moysan 
226cf881773SOlivier Moysan 	return pinctrl_pm_select_sleep_state(dev);
227cf881773SOlivier Moysan }
228cf881773SOlivier Moysan 
229cf881773SOlivier Moysan static int stm32_sai_resume(struct device *dev)
230cf881773SOlivier Moysan {
231cf881773SOlivier Moysan 	struct stm32_sai_data *sai = dev_get_drvdata(dev);
232cf881773SOlivier Moysan 	int ret;
233cf881773SOlivier Moysan 
234cf881773SOlivier Moysan 	ret = stm32_sai_pclk_enable(dev);
235cf881773SOlivier Moysan 	if (ret)
236cf881773SOlivier Moysan 		return ret;
237cf881773SOlivier Moysan 
238cf881773SOlivier Moysan 	writel_relaxed(sai->gcr, sai->base);
239cf881773SOlivier Moysan 	stm32_sai_pclk_disable(dev);
240cf881773SOlivier Moysan 
241cf881773SOlivier Moysan 	return pinctrl_pm_select_default_state(dev);
242cf881773SOlivier Moysan }
243cf881773SOlivier Moysan #endif /* CONFIG_PM_SLEEP */
244cf881773SOlivier Moysan 
245cf881773SOlivier Moysan static const struct dev_pm_ops stm32_sai_pm_ops = {
246cf881773SOlivier Moysan 	SET_SYSTEM_SLEEP_PM_OPS(stm32_sai_suspend, stm32_sai_resume)
247cf881773SOlivier Moysan };
248cf881773SOlivier Moysan 
2493e086edfSolivier moysan MODULE_DEVICE_TABLE(of, stm32_sai_ids);
2503e086edfSolivier moysan 
2513e086edfSolivier moysan static struct platform_driver stm32_sai_driver = {
2523e086edfSolivier moysan 	.driver = {
2533e086edfSolivier moysan 		.name = "st,stm32-sai",
2543e086edfSolivier moysan 		.of_match_table = stm32_sai_ids,
255cf881773SOlivier Moysan 		.pm = &stm32_sai_pm_ops,
2563e086edfSolivier moysan 	},
2573e086edfSolivier moysan 	.probe = stm32_sai_probe,
2583e086edfSolivier moysan };
2593e086edfSolivier moysan 
2603e086edfSolivier moysan module_platform_driver(stm32_sai_driver);
2613e086edfSolivier moysan 
2623e086edfSolivier moysan MODULE_DESCRIPTION("STM32 Soc SAI Interface");
263602fdadcSolivier moysan MODULE_AUTHOR("Olivier Moysan <olivier.moysan@st.com>");
2643e086edfSolivier moysan MODULE_ALIAS("platform:st,stm32-sai");
2653e086edfSolivier moysan MODULE_LICENSE("GPL v2");
266