162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * STM32 ALSA SoC Digital Audio Interface (SAI) driver.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
662306a36Sopenharmony_ci * Author(s): Olivier Moysan <olivier.moysan@st.com> for STMicroelectronics.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/bitfield.h>
1062306a36Sopenharmony_ci#include <linux/clk.h>
1162306a36Sopenharmony_ci#include <linux/delay.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/of_platform.h>
1462306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h>
1562306a36Sopenharmony_ci#include <linux/reset.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <sound/dmaengine_pcm.h>
1862306a36Sopenharmony_ci#include <sound/core.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include "stm32_sai.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic const struct stm32_sai_conf stm32_sai_conf_f4 = {
2362306a36Sopenharmony_ci	.version = STM_SAI_STM32F4,
2462306a36Sopenharmony_ci	.fifo_size = 8,
2562306a36Sopenharmony_ci	.has_spdif_pdm = false,
2662306a36Sopenharmony_ci};
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/*
2962306a36Sopenharmony_ci * Default settings for stm32 H7 socs and next.
3062306a36Sopenharmony_ci * These default settings will be overridden if the soc provides
3162306a36Sopenharmony_ci * support of hardware configuration registers.
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_cistatic const struct stm32_sai_conf stm32_sai_conf_h7 = {
3462306a36Sopenharmony_ci	.version = STM_SAI_STM32H7,
3562306a36Sopenharmony_ci	.fifo_size = 8,
3662306a36Sopenharmony_ci	.has_spdif_pdm = true,
3762306a36Sopenharmony_ci};
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic const struct of_device_id stm32_sai_ids[] = {
4062306a36Sopenharmony_ci	{ .compatible = "st,stm32f4-sai", .data = (void *)&stm32_sai_conf_f4 },
4162306a36Sopenharmony_ci	{ .compatible = "st,stm32h7-sai", .data = (void *)&stm32_sai_conf_h7 },
4262306a36Sopenharmony_ci	{}
4362306a36Sopenharmony_ci};
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic int stm32_sai_pclk_disable(struct device *dev)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	struct stm32_sai_data *sai = dev_get_drvdata(dev);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	clk_disable_unprepare(sai->pclk);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	return 0;
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic int stm32_sai_pclk_enable(struct device *dev)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	struct stm32_sai_data *sai = dev_get_drvdata(dev);
5762306a36Sopenharmony_ci	int ret;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	ret = clk_prepare_enable(sai->pclk);
6062306a36Sopenharmony_ci	if (ret) {
6162306a36Sopenharmony_ci		dev_err(&sai->pdev->dev, "failed to enable clock: %d\n", ret);
6262306a36Sopenharmony_ci		return ret;
6362306a36Sopenharmony_ci	}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	return 0;
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic int stm32_sai_sync_conf_client(struct stm32_sai_data *sai, int synci)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	int ret;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	/* Enable peripheral clock to allow GCR register access */
7362306a36Sopenharmony_ci	ret = stm32_sai_pclk_enable(&sai->pdev->dev);
7462306a36Sopenharmony_ci	if (ret)
7562306a36Sopenharmony_ci		return ret;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	writel_relaxed(FIELD_PREP(SAI_GCR_SYNCIN_MASK, (synci - 1)), sai->base);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	stm32_sai_pclk_disable(&sai->pdev->dev);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	return 0;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic int stm32_sai_sync_conf_provider(struct stm32_sai_data *sai, int synco)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	u32 prev_synco;
8762306a36Sopenharmony_ci	int ret;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	/* Enable peripheral clock to allow GCR register access */
9062306a36Sopenharmony_ci	ret = stm32_sai_pclk_enable(&sai->pdev->dev);
9162306a36Sopenharmony_ci	if (ret)
9262306a36Sopenharmony_ci		return ret;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	dev_dbg(&sai->pdev->dev, "Set %pOFn%s as synchro provider\n",
9562306a36Sopenharmony_ci		sai->pdev->dev.of_node,
9662306a36Sopenharmony_ci		synco == STM_SAI_SYNC_OUT_A ? "A" : "B");
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	prev_synco = FIELD_GET(SAI_GCR_SYNCOUT_MASK, readl_relaxed(sai->base));
9962306a36Sopenharmony_ci	if (prev_synco != STM_SAI_SYNC_OUT_NONE && synco != prev_synco) {
10062306a36Sopenharmony_ci		dev_err(&sai->pdev->dev, "%pOFn%s already set as sync provider\n",
10162306a36Sopenharmony_ci			sai->pdev->dev.of_node,
10262306a36Sopenharmony_ci			prev_synco == STM_SAI_SYNC_OUT_A ? "A" : "B");
10362306a36Sopenharmony_ci		stm32_sai_pclk_disable(&sai->pdev->dev);
10462306a36Sopenharmony_ci		return -EINVAL;
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	writel_relaxed(FIELD_PREP(SAI_GCR_SYNCOUT_MASK, synco), sai->base);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	stm32_sai_pclk_disable(&sai->pdev->dev);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	return 0;
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic int stm32_sai_set_sync(struct stm32_sai_data *sai_client,
11562306a36Sopenharmony_ci			      struct device_node *np_provider,
11662306a36Sopenharmony_ci			      int synco, int synci)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	struct platform_device *pdev = of_find_device_by_node(np_provider);
11962306a36Sopenharmony_ci	struct stm32_sai_data *sai_provider;
12062306a36Sopenharmony_ci	int ret;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	if (!pdev) {
12362306a36Sopenharmony_ci		dev_err(&sai_client->pdev->dev,
12462306a36Sopenharmony_ci			"Device not found for node %pOFn\n", np_provider);
12562306a36Sopenharmony_ci		of_node_put(np_provider);
12662306a36Sopenharmony_ci		return -ENODEV;
12762306a36Sopenharmony_ci	}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	sai_provider = platform_get_drvdata(pdev);
13062306a36Sopenharmony_ci	if (!sai_provider) {
13162306a36Sopenharmony_ci		dev_err(&sai_client->pdev->dev,
13262306a36Sopenharmony_ci			"SAI sync provider data not found\n");
13362306a36Sopenharmony_ci		ret = -EINVAL;
13462306a36Sopenharmony_ci		goto error;
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	/* Configure sync client */
13862306a36Sopenharmony_ci	ret = stm32_sai_sync_conf_client(sai_client, synci);
13962306a36Sopenharmony_ci	if (ret < 0)
14062306a36Sopenharmony_ci		goto error;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	/* Configure sync provider */
14362306a36Sopenharmony_ci	ret = stm32_sai_sync_conf_provider(sai_provider, synco);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cierror:
14662306a36Sopenharmony_ci	put_device(&pdev->dev);
14762306a36Sopenharmony_ci	of_node_put(np_provider);
14862306a36Sopenharmony_ci	return ret;
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic int stm32_sai_probe(struct platform_device *pdev)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	struct stm32_sai_data *sai;
15462306a36Sopenharmony_ci	struct reset_control *rst;
15562306a36Sopenharmony_ci	const struct of_device_id *of_id;
15662306a36Sopenharmony_ci	u32 val;
15762306a36Sopenharmony_ci	int ret;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
16062306a36Sopenharmony_ci	if (!sai)
16162306a36Sopenharmony_ci		return -ENOMEM;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	sai->base = devm_platform_ioremap_resource(pdev, 0);
16462306a36Sopenharmony_ci	if (IS_ERR(sai->base))
16562306a36Sopenharmony_ci		return PTR_ERR(sai->base);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	of_id = of_match_device(stm32_sai_ids, &pdev->dev);
16862306a36Sopenharmony_ci	if (of_id)
16962306a36Sopenharmony_ci		memcpy(&sai->conf, (const struct stm32_sai_conf *)of_id->data,
17062306a36Sopenharmony_ci		       sizeof(struct stm32_sai_conf));
17162306a36Sopenharmony_ci	else
17262306a36Sopenharmony_ci		return -EINVAL;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	if (!STM_SAI_IS_F4(sai)) {
17562306a36Sopenharmony_ci		sai->pclk = devm_clk_get(&pdev->dev, "pclk");
17662306a36Sopenharmony_ci		if (IS_ERR(sai->pclk))
17762306a36Sopenharmony_ci			return dev_err_probe(&pdev->dev, PTR_ERR(sai->pclk),
17862306a36Sopenharmony_ci					     "missing bus clock pclk\n");
17962306a36Sopenharmony_ci	}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	sai->clk_x8k = devm_clk_get(&pdev->dev, "x8k");
18262306a36Sopenharmony_ci	if (IS_ERR(sai->clk_x8k))
18362306a36Sopenharmony_ci		return dev_err_probe(&pdev->dev, PTR_ERR(sai->clk_x8k),
18462306a36Sopenharmony_ci				     "missing x8k parent clock\n");
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	sai->clk_x11k = devm_clk_get(&pdev->dev, "x11k");
18762306a36Sopenharmony_ci	if (IS_ERR(sai->clk_x11k))
18862306a36Sopenharmony_ci		return dev_err_probe(&pdev->dev, PTR_ERR(sai->clk_x11k),
18962306a36Sopenharmony_ci				     "missing x11k parent clock\n");
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	/* init irqs */
19262306a36Sopenharmony_ci	sai->irq = platform_get_irq(pdev, 0);
19362306a36Sopenharmony_ci	if (sai->irq < 0)
19462306a36Sopenharmony_ci		return sai->irq;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	/* reset */
19762306a36Sopenharmony_ci	rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL);
19862306a36Sopenharmony_ci	if (IS_ERR(rst))
19962306a36Sopenharmony_ci		return dev_err_probe(&pdev->dev, PTR_ERR(rst),
20062306a36Sopenharmony_ci				     "Reset controller error\n");
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	reset_control_assert(rst);
20362306a36Sopenharmony_ci	udelay(2);
20462306a36Sopenharmony_ci	reset_control_deassert(rst);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	/* Enable peripheral clock to allow register access */
20762306a36Sopenharmony_ci	ret = clk_prepare_enable(sai->pclk);
20862306a36Sopenharmony_ci	if (ret) {
20962306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to enable clock: %d\n", ret);
21062306a36Sopenharmony_ci		return ret;
21162306a36Sopenharmony_ci	}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	val = FIELD_GET(SAI_IDR_ID_MASK,
21462306a36Sopenharmony_ci			readl_relaxed(sai->base + STM_SAI_IDR));
21562306a36Sopenharmony_ci	if (val == SAI_IPIDR_NUMBER) {
21662306a36Sopenharmony_ci		val = readl_relaxed(sai->base + STM_SAI_HWCFGR);
21762306a36Sopenharmony_ci		sai->conf.fifo_size = FIELD_GET(SAI_HWCFGR_FIFO_SIZE, val);
21862306a36Sopenharmony_ci		sai->conf.has_spdif_pdm = !!FIELD_GET(SAI_HWCFGR_SPDIF_PDM,
21962306a36Sopenharmony_ci						      val);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci		val = readl_relaxed(sai->base + STM_SAI_VERR);
22262306a36Sopenharmony_ci		sai->conf.version = val;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci		dev_dbg(&pdev->dev, "SAI version: %lu.%lu registered\n",
22562306a36Sopenharmony_ci			FIELD_GET(SAI_VERR_MAJ_MASK, val),
22662306a36Sopenharmony_ci			FIELD_GET(SAI_VERR_MIN_MASK, val));
22762306a36Sopenharmony_ci	}
22862306a36Sopenharmony_ci	clk_disable_unprepare(sai->pclk);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	sai->pdev = pdev;
23162306a36Sopenharmony_ci	sai->set_sync = &stm32_sai_set_sync;
23262306a36Sopenharmony_ci	platform_set_drvdata(pdev, sai);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	return devm_of_platform_populate(&pdev->dev);
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
23862306a36Sopenharmony_ci/*
23962306a36Sopenharmony_ci * When pins are shared by two sai sub instances, pins have to be defined
24062306a36Sopenharmony_ci * in sai parent node. In this case, pins state is not managed by alsa fw.
24162306a36Sopenharmony_ci * These pins are managed in suspend/resume callbacks.
24262306a36Sopenharmony_ci */
24362306a36Sopenharmony_cistatic int stm32_sai_suspend(struct device *dev)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	struct stm32_sai_data *sai = dev_get_drvdata(dev);
24662306a36Sopenharmony_ci	int ret;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	ret = stm32_sai_pclk_enable(dev);
24962306a36Sopenharmony_ci	if (ret)
25062306a36Sopenharmony_ci		return ret;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	sai->gcr = readl_relaxed(sai->base);
25362306a36Sopenharmony_ci	stm32_sai_pclk_disable(dev);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	return pinctrl_pm_select_sleep_state(dev);
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_cistatic int stm32_sai_resume(struct device *dev)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	struct stm32_sai_data *sai = dev_get_drvdata(dev);
26162306a36Sopenharmony_ci	int ret;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	ret = stm32_sai_pclk_enable(dev);
26462306a36Sopenharmony_ci	if (ret)
26562306a36Sopenharmony_ci		return ret;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	writel_relaxed(sai->gcr, sai->base);
26862306a36Sopenharmony_ci	stm32_sai_pclk_disable(dev);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	return pinctrl_pm_select_default_state(dev);
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_cistatic const struct dev_pm_ops stm32_sai_pm_ops = {
27562306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(stm32_sai_suspend, stm32_sai_resume)
27662306a36Sopenharmony_ci};
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, stm32_sai_ids);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic struct platform_driver stm32_sai_driver = {
28162306a36Sopenharmony_ci	.driver = {
28262306a36Sopenharmony_ci		.name = "st,stm32-sai",
28362306a36Sopenharmony_ci		.of_match_table = stm32_sai_ids,
28462306a36Sopenharmony_ci		.pm = &stm32_sai_pm_ops,
28562306a36Sopenharmony_ci	},
28662306a36Sopenharmony_ci	.probe = stm32_sai_probe,
28762306a36Sopenharmony_ci};
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_cimodule_platform_driver(stm32_sai_driver);
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ciMODULE_DESCRIPTION("STM32 Soc SAI Interface");
29262306a36Sopenharmony_ciMODULE_AUTHOR("Olivier Moysan <olivier.moysan@st.com>");
29362306a36Sopenharmony_ciMODULE_ALIAS("platform:st,stm32-sai");
29462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
295