diff options
Diffstat (limited to 'sound/soc/stm/stm32_sai.c')
-rw-r--r-- | sound/soc/stm/stm32_sai.c | 110 |
1 files changed, 95 insertions, 15 deletions
diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c index 1258bef4dcb3..d743b7dd52fb 100644 --- a/sound/soc/stm/stm32_sai.c +++ b/sound/soc/stm/stm32_sai.c @@ -16,6 +16,7 @@ * details. */ +#include <linux/bitfield.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/module.h> @@ -41,13 +42,91 @@ static const struct of_device_id stm32_sai_ids[] = { {} }; +static int stm32_sai_sync_conf_client(struct stm32_sai_data *sai, int synci) +{ + int ret; + + /* Enable peripheral clock to allow GCR register access */ + ret = clk_prepare_enable(sai->pclk); + if (ret) { + dev_err(&sai->pdev->dev, "failed to enable clock: %d\n", ret); + return ret; + } + + writel_relaxed(FIELD_PREP(SAI_GCR_SYNCIN_MASK, (synci - 1)), sai->base); + + clk_disable_unprepare(sai->pclk); + + return 0; +} + +static int stm32_sai_sync_conf_provider(struct stm32_sai_data *sai, int synco) +{ + u32 prev_synco; + int ret; + + /* Enable peripheral clock to allow GCR register access */ + ret = clk_prepare_enable(sai->pclk); + if (ret) { + dev_err(&sai->pdev->dev, "failed to enable clock: %d\n", ret); + return ret; + } + + dev_dbg(&sai->pdev->dev, "Set %s%s as synchro provider\n", + sai->pdev->dev.of_node->name, + synco == STM_SAI_SYNC_OUT_A ? "A" : "B"); + + prev_synco = FIELD_GET(SAI_GCR_SYNCOUT_MASK, readl_relaxed(sai->base)); + if (prev_synco != STM_SAI_SYNC_OUT_NONE && synco != prev_synco) { + dev_err(&sai->pdev->dev, "%s%s already set as sync provider\n", + sai->pdev->dev.of_node->name, + prev_synco == STM_SAI_SYNC_OUT_A ? "A" : "B"); + clk_disable_unprepare(sai->pclk); + return -EINVAL; + } + + writel_relaxed(FIELD_PREP(SAI_GCR_SYNCOUT_MASK, synco), sai->base); + + clk_disable_unprepare(sai->pclk); + + return 0; +} + +static int stm32_sai_set_sync(struct stm32_sai_data *sai_client, + struct device_node *np_provider, + int synco, int synci) +{ + struct platform_device *pdev = of_find_device_by_node(np_provider); + struct stm32_sai_data *sai_provider; + int ret; + + if (!pdev) { + dev_err(&sai_client->pdev->dev, + "Device not found for node %s\n", np_provider->name); + return -ENODEV; + } + + sai_provider = platform_get_drvdata(pdev); + if (!sai_provider) { + dev_err(&sai_client->pdev->dev, + "SAI sync provider data not found\n"); + return -EINVAL; + } + + /* Configure sync client */ + ret = stm32_sai_sync_conf_client(sai_client, synci); + if (ret < 0) + return ret; + + /* Configure sync provider */ + return stm32_sai_sync_conf_provider(sai_provider, synco); +} + static int stm32_sai_probe(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; struct stm32_sai_data *sai; struct reset_control *rst; struct resource *res; - void __iomem *base; const struct of_device_id *of_id; sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); @@ -55,9 +134,9 @@ static int stm32_sai_probe(struct platform_device *pdev) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); + sai->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(sai->base)) + return PTR_ERR(sai->base); of_id = of_match_device(stm32_sai_ids, &pdev->dev); if (of_id) @@ -65,6 +144,14 @@ static int stm32_sai_probe(struct platform_device *pdev) else return -EINVAL; + if (!STM_SAI_IS_F4(sai)) { + sai->pclk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(sai->pclk)) { + dev_err(&pdev->dev, "missing bus clock pclk\n"); + return PTR_ERR(sai->pclk); + } + } + sai->clk_x8k = devm_clk_get(&pdev->dev, "x8k"); if (IS_ERR(sai->clk_x8k)) { dev_err(&pdev->dev, "missing x8k parent clock\n"); @@ -85,7 +172,7 @@ static int stm32_sai_probe(struct platform_device *pdev) } /* reset */ - rst = reset_control_get_exclusive(&pdev->dev, NULL); + rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (!IS_ERR(rst)) { reset_control_assert(rst); udelay(2); @@ -93,16 +180,10 @@ static int stm32_sai_probe(struct platform_device *pdev) } sai->pdev = pdev; + sai->set_sync = &stm32_sai_set_sync; platform_set_drvdata(pdev, sai); - return of_platform_populate(np, NULL, NULL, &pdev->dev); -} - -static int stm32_sai_remove(struct platform_device *pdev) -{ - of_platform_depopulate(&pdev->dev); - - return 0; + return devm_of_platform_populate(&pdev->dev); } MODULE_DEVICE_TABLE(of, stm32_sai_ids); @@ -113,7 +194,6 @@ static struct platform_driver stm32_sai_driver = { .of_match_table = stm32_sai_ids, }, .probe = stm32_sai_probe, - .remove = stm32_sai_remove, }; module_platform_driver(stm32_sai_driver); |