xref: /openbmc/linux/sound/soc/stm/stm32_sai.c (revision efc162cb)
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 = {
231d9c95c1SOlivier Moysan 	.version = STM_SAI_STM32F4,
241d9c95c1SOlivier Moysan 	.fifo_size = 8,
251d9c95c1SOlivier Moysan 	.has_spdif_pdm = false,
2603e78a24Solivier moysan };
2703e78a24Solivier moysan 
281d9c95c1SOlivier Moysan /*
291d9c95c1SOlivier Moysan  * Default settings for stm32 H7 socs and next.
301d9c95c1SOlivier Moysan  * These default settings will be overridden if the soc provides
311d9c95c1SOlivier Moysan  * support of hardware configuration registers.
321d9c95c1SOlivier Moysan  */
3303e78a24Solivier moysan static const struct stm32_sai_conf stm32_sai_conf_h7 = {
341d9c95c1SOlivier Moysan 	.version = STM_SAI_STM32H7,
351d9c95c1SOlivier Moysan 	.fifo_size = 8,
361d9c95c1SOlivier Moysan 	.has_spdif_pdm = true,
3703e78a24Solivier moysan };
3803e78a24Solivier moysan 
393e086edfSolivier moysan static const struct of_device_id stm32_sai_ids[] = {
4003e78a24Solivier moysan 	{ .compatible = "st,stm32f4-sai", .data = (void *)&stm32_sai_conf_f4 },
4103e78a24Solivier moysan 	{ .compatible = "st,stm32h7-sai", .data = (void *)&stm32_sai_conf_h7 },
423e086edfSolivier moysan 	{}
433e086edfSolivier moysan };
443e086edfSolivier moysan 
stm32_sai_pclk_disable(struct device * dev)45cf881773SOlivier Moysan static int stm32_sai_pclk_disable(struct device *dev)
465914d285SOlivier Moysan {
47cf881773SOlivier Moysan 	struct stm32_sai_data *sai = dev_get_drvdata(dev);
48cf881773SOlivier Moysan 
49cf881773SOlivier Moysan 	clk_disable_unprepare(sai->pclk);
50cf881773SOlivier Moysan 
51cf881773SOlivier Moysan 	return 0;
52cf881773SOlivier Moysan }
53cf881773SOlivier Moysan 
stm32_sai_pclk_enable(struct device * dev)54cf881773SOlivier Moysan static int stm32_sai_pclk_enable(struct device *dev)
55cf881773SOlivier Moysan {
56cf881773SOlivier Moysan 	struct stm32_sai_data *sai = dev_get_drvdata(dev);
575914d285SOlivier Moysan 	int ret;
585914d285SOlivier Moysan 
595914d285SOlivier Moysan 	ret = clk_prepare_enable(sai->pclk);
605914d285SOlivier Moysan 	if (ret) {
615914d285SOlivier Moysan 		dev_err(&sai->pdev->dev, "failed to enable clock: %d\n", ret);
625914d285SOlivier Moysan 		return ret;
635914d285SOlivier Moysan 	}
645914d285SOlivier Moysan 
65cf881773SOlivier Moysan 	return 0;
66cf881773SOlivier Moysan }
67cf881773SOlivier Moysan 
stm32_sai_sync_conf_client(struct stm32_sai_data * sai,int synci)68cf881773SOlivier Moysan static int stm32_sai_sync_conf_client(struct stm32_sai_data *sai, int synci)
69cf881773SOlivier Moysan {
70cf881773SOlivier Moysan 	int ret;
71cf881773SOlivier Moysan 
72cf881773SOlivier Moysan 	/* Enable peripheral clock to allow GCR register access */
73cf881773SOlivier Moysan 	ret = stm32_sai_pclk_enable(&sai->pdev->dev);
74cf881773SOlivier Moysan 	if (ret)
75cf881773SOlivier Moysan 		return ret;
76cf881773SOlivier Moysan 
775914d285SOlivier Moysan 	writel_relaxed(FIELD_PREP(SAI_GCR_SYNCIN_MASK, (synci - 1)), sai->base);
785914d285SOlivier Moysan 
79cf881773SOlivier Moysan 	stm32_sai_pclk_disable(&sai->pdev->dev);
805914d285SOlivier Moysan 
815914d285SOlivier Moysan 	return 0;
825914d285SOlivier Moysan }
835914d285SOlivier Moysan 
stm32_sai_sync_conf_provider(struct stm32_sai_data * sai,int synco)847dd0d835Solivier moysan static int stm32_sai_sync_conf_provider(struct stm32_sai_data *sai, int synco)
855914d285SOlivier Moysan {
865914d285SOlivier Moysan 	u32 prev_synco;
875914d285SOlivier Moysan 	int ret;
885914d285SOlivier Moysan 
895914d285SOlivier Moysan 	/* Enable peripheral clock to allow GCR register access */
90cf881773SOlivier Moysan 	ret = stm32_sai_pclk_enable(&sai->pdev->dev);
91cf881773SOlivier Moysan 	if (ret)
925914d285SOlivier Moysan 		return ret;
935914d285SOlivier Moysan 
94dc43d3aaSRob Herring 	dev_dbg(&sai->pdev->dev, "Set %pOFn%s as synchro provider\n",
95dc43d3aaSRob Herring 		sai->pdev->dev.of_node,
965914d285SOlivier Moysan 		synco == STM_SAI_SYNC_OUT_A ? "A" : "B");
975914d285SOlivier Moysan 
985914d285SOlivier Moysan 	prev_synco = FIELD_GET(SAI_GCR_SYNCOUT_MASK, readl_relaxed(sai->base));
995914d285SOlivier Moysan 	if (prev_synco != STM_SAI_SYNC_OUT_NONE && synco != prev_synco) {
100dc43d3aaSRob Herring 		dev_err(&sai->pdev->dev, "%pOFn%s already set as sync provider\n",
101dc43d3aaSRob Herring 			sai->pdev->dev.of_node,
1025914d285SOlivier Moysan 			prev_synco == STM_SAI_SYNC_OUT_A ? "A" : "B");
103cf881773SOlivier Moysan 		stm32_sai_pclk_disable(&sai->pdev->dev);
1045914d285SOlivier Moysan 		return -EINVAL;
1055914d285SOlivier Moysan 	}
1065914d285SOlivier Moysan 
1075914d285SOlivier Moysan 	writel_relaxed(FIELD_PREP(SAI_GCR_SYNCOUT_MASK, synco), sai->base);
1085914d285SOlivier Moysan 
109cf881773SOlivier Moysan 	stm32_sai_pclk_disable(&sai->pdev->dev);
1105914d285SOlivier Moysan 
1115914d285SOlivier Moysan 	return 0;
1125914d285SOlivier Moysan }
1135914d285SOlivier Moysan 
stm32_sai_set_sync(struct stm32_sai_data * sai_client,struct device_node * np_provider,int synco,int synci)1147dd0d835Solivier moysan static int stm32_sai_set_sync(struct stm32_sai_data *sai_client,
1155914d285SOlivier Moysan 			      struct device_node *np_provider,
1165914d285SOlivier Moysan 			      int synco, int synci)
1175914d285SOlivier Moysan {
1187dd0d835Solivier moysan 	struct platform_device *pdev = of_find_device_by_node(np_provider);
1197dd0d835Solivier moysan 	struct stm32_sai_data *sai_provider;
1205914d285SOlivier Moysan 	int ret;
1215914d285SOlivier Moysan 
1227dd0d835Solivier moysan 	if (!pdev) {
1237dd0d835Solivier moysan 		dev_err(&sai_client->pdev->dev,
1245d585e1eSRob Herring 			"Device not found for node %pOFn\n", np_provider);
125d4180b4cSOlivier Moysan 		of_node_put(np_provider);
1267dd0d835Solivier moysan 		return -ENODEV;
1277dd0d835Solivier moysan 	}
1287dd0d835Solivier moysan 
1297dd0d835Solivier moysan 	sai_provider = platform_get_drvdata(pdev);
1307dd0d835Solivier moysan 	if (!sai_provider) {
1317dd0d835Solivier moysan 		dev_err(&sai_client->pdev->dev,
1327dd0d835Solivier moysan 			"SAI sync provider data not found\n");
1331c3816a1SWen Yang 		ret = -EINVAL;
134d4180b4cSOlivier Moysan 		goto error;
1357dd0d835Solivier moysan 	}
1367dd0d835Solivier moysan 
1375914d285SOlivier Moysan 	/* Configure sync client */
1387dd0d835Solivier moysan 	ret = stm32_sai_sync_conf_client(sai_client, synci);
1397dd0d835Solivier moysan 	if (ret < 0)
140d4180b4cSOlivier Moysan 		goto error;
1415914d285SOlivier Moysan 
1425914d285SOlivier Moysan 	/* Configure sync provider */
1431c3816a1SWen Yang 	ret = stm32_sai_sync_conf_provider(sai_provider, synco);
1441c3816a1SWen Yang 
145d4180b4cSOlivier Moysan error:
1461c3816a1SWen Yang 	put_device(&pdev->dev);
147d4180b4cSOlivier Moysan 	of_node_put(np_provider);
1481c3816a1SWen Yang 	return ret;
1495914d285SOlivier Moysan }
1505914d285SOlivier Moysan 
stm32_sai_probe(struct platform_device * pdev)1513e086edfSolivier moysan static int stm32_sai_probe(struct platform_device *pdev)
1523e086edfSolivier moysan {
1533e086edfSolivier moysan 	struct stm32_sai_data *sai;
1543e086edfSolivier moysan 	struct reset_control *rst;
1553e086edfSolivier moysan 	const struct of_device_id *of_id;
1561d9c95c1SOlivier Moysan 	u32 val;
1571d9c95c1SOlivier Moysan 	int ret;
1583e086edfSolivier moysan 
1593e086edfSolivier moysan 	sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
1603e086edfSolivier moysan 	if (!sai)
1613e086edfSolivier moysan 		return -ENOMEM;
1623e086edfSolivier moysan 
1631c63223cSYueHaibing 	sai->base = devm_platform_ioremap_resource(pdev, 0);
1645914d285SOlivier Moysan 	if (IS_ERR(sai->base))
1655914d285SOlivier Moysan 		return PTR_ERR(sai->base);
1663e086edfSolivier moysan 
1673e086edfSolivier moysan 	of_id = of_match_device(stm32_sai_ids, &pdev->dev);
1683e086edfSolivier moysan 	if (of_id)
1691d9c95c1SOlivier Moysan 		memcpy(&sai->conf, (const struct stm32_sai_conf *)of_id->data,
1701d9c95c1SOlivier Moysan 		       sizeof(struct stm32_sai_conf));
1713e086edfSolivier moysan 	else
1723e086edfSolivier moysan 		return -EINVAL;
1733e086edfSolivier moysan 
1745914d285SOlivier Moysan 	if (!STM_SAI_IS_F4(sai)) {
1755914d285SOlivier Moysan 		sai->pclk = devm_clk_get(&pdev->dev, "pclk");
176*efc162cbSKuninori Morimoto 		if (IS_ERR(sai->pclk))
177*efc162cbSKuninori Morimoto 			return dev_err_probe(&pdev->dev, PTR_ERR(sai->pclk),
178*efc162cbSKuninori Morimoto 					     "missing bus clock pclk\n");
1795914d285SOlivier Moysan 	}
1805914d285SOlivier Moysan 
1813e086edfSolivier moysan 	sai->clk_x8k = devm_clk_get(&pdev->dev, "x8k");
182*efc162cbSKuninori Morimoto 	if (IS_ERR(sai->clk_x8k))
183*efc162cbSKuninori Morimoto 		return dev_err_probe(&pdev->dev, PTR_ERR(sai->clk_x8k),
184*efc162cbSKuninori Morimoto 				     "missing x8k parent clock\n");
1853e086edfSolivier moysan 
1863e086edfSolivier moysan 	sai->clk_x11k = devm_clk_get(&pdev->dev, "x11k");
187*efc162cbSKuninori Morimoto 	if (IS_ERR(sai->clk_x11k))
188*efc162cbSKuninori Morimoto 		return dev_err_probe(&pdev->dev, PTR_ERR(sai->clk_x11k),
189*efc162cbSKuninori Morimoto 				     "missing x11k parent clock\n");
1903e086edfSolivier moysan 
1913e086edfSolivier moysan 	/* init irqs */
1923e086edfSolivier moysan 	sai->irq = platform_get_irq(pdev, 0);
193cf9441adSStephen Boyd 	if (sai->irq < 0)
1943e086edfSolivier moysan 		return sai->irq;
1953e086edfSolivier moysan 
1963e086edfSolivier moysan 	/* reset */
197028476c8SOlivier Moysan 	rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL);
198*efc162cbSKuninori Morimoto 	if (IS_ERR(rst))
199*efc162cbSKuninori Morimoto 		return dev_err_probe(&pdev->dev, PTR_ERR(rst),
200*efc162cbSKuninori Morimoto 				     "Reset controller error\n");
201*efc162cbSKuninori Morimoto 
2023e086edfSolivier moysan 	reset_control_assert(rst);
2033e086edfSolivier moysan 	udelay(2);
2043e086edfSolivier moysan 	reset_control_deassert(rst);
2053e086edfSolivier moysan 
2061d9c95c1SOlivier Moysan 	/* Enable peripheral clock to allow register access */
2071d9c95c1SOlivier Moysan 	ret = clk_prepare_enable(sai->pclk);
2081d9c95c1SOlivier Moysan 	if (ret) {
2091d9c95c1SOlivier Moysan 		dev_err(&pdev->dev, "failed to enable clock: %d\n", ret);
2101d9c95c1SOlivier Moysan 		return ret;
2111d9c95c1SOlivier Moysan 	}
2121d9c95c1SOlivier Moysan 
2131d9c95c1SOlivier Moysan 	val = FIELD_GET(SAI_IDR_ID_MASK,
2141d9c95c1SOlivier Moysan 			readl_relaxed(sai->base + STM_SAI_IDR));
2151d9c95c1SOlivier Moysan 	if (val == SAI_IPIDR_NUMBER) {
2161d9c95c1SOlivier Moysan 		val = readl_relaxed(sai->base + STM_SAI_HWCFGR);
2171d9c95c1SOlivier Moysan 		sai->conf.fifo_size = FIELD_GET(SAI_HWCFGR_FIFO_SIZE, val);
2181d9c95c1SOlivier Moysan 		sai->conf.has_spdif_pdm = !!FIELD_GET(SAI_HWCFGR_SPDIF_PDM,
2191d9c95c1SOlivier Moysan 						      val);
2201d9c95c1SOlivier Moysan 
2211d9c95c1SOlivier Moysan 		val = readl_relaxed(sai->base + STM_SAI_VERR);
2221d9c95c1SOlivier Moysan 		sai->conf.version = val;
2231d9c95c1SOlivier Moysan 
2241d9c95c1SOlivier Moysan 		dev_dbg(&pdev->dev, "SAI version: %lu.%lu registered\n",
2251d9c95c1SOlivier Moysan 			FIELD_GET(SAI_VERR_MAJ_MASK, val),
2261d9c95c1SOlivier Moysan 			FIELD_GET(SAI_VERR_MIN_MASK, val));
2271d9c95c1SOlivier Moysan 	}
2281d9c95c1SOlivier Moysan 	clk_disable_unprepare(sai->pclk);
2291d9c95c1SOlivier Moysan 
2303e086edfSolivier moysan 	sai->pdev = pdev;
2317dd0d835Solivier moysan 	sai->set_sync = &stm32_sai_set_sync;
2323e086edfSolivier moysan 	platform_set_drvdata(pdev, sai);
2333e086edfSolivier moysan 
234512d1bb4Solivier moysan 	return devm_of_platform_populate(&pdev->dev);
2353e086edfSolivier moysan }
2363e086edfSolivier moysan 
237cf881773SOlivier Moysan #ifdef CONFIG_PM_SLEEP
238cf881773SOlivier Moysan /*
239cf881773SOlivier Moysan  * When pins are shared by two sai sub instances, pins have to be defined
240cf881773SOlivier Moysan  * in sai parent node. In this case, pins state is not managed by alsa fw.
241cf881773SOlivier Moysan  * These pins are managed in suspend/resume callbacks.
242cf881773SOlivier Moysan  */
stm32_sai_suspend(struct device * dev)243cf881773SOlivier Moysan static int stm32_sai_suspend(struct device *dev)
244cf881773SOlivier Moysan {
245cf881773SOlivier Moysan 	struct stm32_sai_data *sai = dev_get_drvdata(dev);
246cf881773SOlivier Moysan 	int ret;
247cf881773SOlivier Moysan 
248cf881773SOlivier Moysan 	ret = stm32_sai_pclk_enable(dev);
249cf881773SOlivier Moysan 	if (ret)
250cf881773SOlivier Moysan 		return ret;
251cf881773SOlivier Moysan 
252cf881773SOlivier Moysan 	sai->gcr = readl_relaxed(sai->base);
253cf881773SOlivier Moysan 	stm32_sai_pclk_disable(dev);
254cf881773SOlivier Moysan 
255cf881773SOlivier Moysan 	return pinctrl_pm_select_sleep_state(dev);
256cf881773SOlivier Moysan }
257cf881773SOlivier Moysan 
stm32_sai_resume(struct device * dev)258cf881773SOlivier Moysan static int stm32_sai_resume(struct device *dev)
259cf881773SOlivier Moysan {
260cf881773SOlivier Moysan 	struct stm32_sai_data *sai = dev_get_drvdata(dev);
261cf881773SOlivier Moysan 	int ret;
262cf881773SOlivier Moysan 
263cf881773SOlivier Moysan 	ret = stm32_sai_pclk_enable(dev);
264cf881773SOlivier Moysan 	if (ret)
265cf881773SOlivier Moysan 		return ret;
266cf881773SOlivier Moysan 
267cf881773SOlivier Moysan 	writel_relaxed(sai->gcr, sai->base);
268cf881773SOlivier Moysan 	stm32_sai_pclk_disable(dev);
269cf881773SOlivier Moysan 
270cf881773SOlivier Moysan 	return pinctrl_pm_select_default_state(dev);
271cf881773SOlivier Moysan }
272cf881773SOlivier Moysan #endif /* CONFIG_PM_SLEEP */
273cf881773SOlivier Moysan 
274cf881773SOlivier Moysan static const struct dev_pm_ops stm32_sai_pm_ops = {
275cf881773SOlivier Moysan 	SET_SYSTEM_SLEEP_PM_OPS(stm32_sai_suspend, stm32_sai_resume)
276cf881773SOlivier Moysan };
277cf881773SOlivier Moysan 
2783e086edfSolivier moysan MODULE_DEVICE_TABLE(of, stm32_sai_ids);
2793e086edfSolivier moysan 
2803e086edfSolivier moysan static struct platform_driver stm32_sai_driver = {
2813e086edfSolivier moysan 	.driver = {
2823e086edfSolivier moysan 		.name = "st,stm32-sai",
2833e086edfSolivier moysan 		.of_match_table = stm32_sai_ids,
284cf881773SOlivier Moysan 		.pm = &stm32_sai_pm_ops,
2853e086edfSolivier moysan 	},
2863e086edfSolivier moysan 	.probe = stm32_sai_probe,
2873e086edfSolivier moysan };
2883e086edfSolivier moysan 
2893e086edfSolivier moysan module_platform_driver(stm32_sai_driver);
2903e086edfSolivier moysan 
2913e086edfSolivier moysan MODULE_DESCRIPTION("STM32 Soc SAI Interface");
292602fdadcSolivier moysan MODULE_AUTHOR("Olivier Moysan <olivier.moysan@st.com>");
2933e086edfSolivier moysan MODULE_ALIAS("platform:st,stm32-sai");
2943e086edfSolivier moysan MODULE_LICENSE("GPL v2");
295