18a24c834SShengjiu Wang // SPDX-License-Identifier: GPL-2.0+
28a24c834SShengjiu Wang // Copyright 2020 NXP
38a24c834SShengjiu Wang
48a24c834SShengjiu Wang #include <linux/clk.h>
58a24c834SShengjiu Wang #include <linux/clk-provider.h>
68a24c834SShengjiu Wang #include <linux/delay.h>
78a24c834SShengjiu Wang #include <linux/dmaengine.h>
8340d79a1SRob Herring #include <linux/mod_devicetable.h>
98a24c834SShengjiu Wang #include <linux/module.h>
108a24c834SShengjiu Wang #include <linux/pm_runtime.h>
118a24c834SShengjiu Wang #include <linux/regmap.h>
128a24c834SShengjiu Wang #include <linux/slab.h>
138a24c834SShengjiu Wang #include <linux/time.h>
148a24c834SShengjiu Wang #include <linux/pm_qos.h>
158a24c834SShengjiu Wang #include <sound/core.h>
168a24c834SShengjiu Wang #include <sound/dmaengine_pcm.h>
178a24c834SShengjiu Wang #include <sound/pcm_params.h>
188a24c834SShengjiu Wang #include <linux/dma-mapping.h>
198a24c834SShengjiu Wang
208a24c834SShengjiu Wang #include "fsl_aud2htx.h"
218a24c834SShengjiu Wang #include "imx-pcm.h"
228a24c834SShengjiu Wang
fsl_aud2htx_trigger(struct snd_pcm_substream * substream,int cmd,struct snd_soc_dai * dai)238a24c834SShengjiu Wang static int fsl_aud2htx_trigger(struct snd_pcm_substream *substream, int cmd,
248a24c834SShengjiu Wang struct snd_soc_dai *dai)
258a24c834SShengjiu Wang {
268a24c834SShengjiu Wang struct fsl_aud2htx *aud2htx = snd_soc_dai_get_drvdata(dai);
278a24c834SShengjiu Wang
288a24c834SShengjiu Wang switch (cmd) {
298a24c834SShengjiu Wang case SNDRV_PCM_TRIGGER_START:
308a24c834SShengjiu Wang case SNDRV_PCM_TRIGGER_RESUME:
318a24c834SShengjiu Wang case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
328a24c834SShengjiu Wang regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL,
338a24c834SShengjiu Wang AUD2HTX_CTRL_EN, AUD2HTX_CTRL_EN);
348a24c834SShengjiu Wang regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT,
358a24c834SShengjiu Wang AUD2HTX_CTRE_DE, AUD2HTX_CTRE_DE);
368a24c834SShengjiu Wang break;
378a24c834SShengjiu Wang case SNDRV_PCM_TRIGGER_SUSPEND:
388a24c834SShengjiu Wang case SNDRV_PCM_TRIGGER_STOP:
398a24c834SShengjiu Wang case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
408a24c834SShengjiu Wang regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT,
418a24c834SShengjiu Wang AUD2HTX_CTRE_DE, 0);
428a24c834SShengjiu Wang regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL,
438a24c834SShengjiu Wang AUD2HTX_CTRL_EN, 0);
448a24c834SShengjiu Wang break;
458a24c834SShengjiu Wang default:
468a24c834SShengjiu Wang return -EINVAL;
478a24c834SShengjiu Wang }
488a24c834SShengjiu Wang return 0;
498a24c834SShengjiu Wang }
508a24c834SShengjiu Wang
fsl_aud2htx_dai_probe(struct snd_soc_dai * cpu_dai)518a24c834SShengjiu Wang static int fsl_aud2htx_dai_probe(struct snd_soc_dai *cpu_dai)
528a24c834SShengjiu Wang {
538a24c834SShengjiu Wang struct fsl_aud2htx *aud2htx = dev_get_drvdata(cpu_dai->dev);
548a24c834SShengjiu Wang
558a24c834SShengjiu Wang /* DMA request when number of entries < WTMK_LOW */
568a24c834SShengjiu Wang regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT,
578a24c834SShengjiu Wang AUD2HTX_CTRE_DT_MASK, 0);
588a24c834SShengjiu Wang
598a24c834SShengjiu Wang /* Disable interrupts*/
608a24c834SShengjiu Wang regmap_update_bits(aud2htx->regmap, AUD2HTX_IRQ_MASK,
618a24c834SShengjiu Wang AUD2HTX_WM_HIGH_IRQ_MASK |
628a24c834SShengjiu Wang AUD2HTX_WM_LOW_IRQ_MASK |
638a24c834SShengjiu Wang AUD2HTX_OVF_MASK,
648a24c834SShengjiu Wang AUD2HTX_WM_HIGH_IRQ_MASK |
658a24c834SShengjiu Wang AUD2HTX_WM_LOW_IRQ_MASK |
668a24c834SShengjiu Wang AUD2HTX_OVF_MASK);
678a24c834SShengjiu Wang
688a24c834SShengjiu Wang /* Configure watermark */
698a24c834SShengjiu Wang regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT,
708a24c834SShengjiu Wang AUD2HTX_CTRE_WL_MASK,
718a24c834SShengjiu Wang AUD2HTX_WTMK_LOW << AUD2HTX_CTRE_WL_SHIFT);
728a24c834SShengjiu Wang regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT,
738a24c834SShengjiu Wang AUD2HTX_CTRE_WH_MASK,
748a24c834SShengjiu Wang AUD2HTX_WTMK_HIGH << AUD2HTX_CTRE_WH_SHIFT);
758a24c834SShengjiu Wang
768a24c834SShengjiu Wang snd_soc_dai_init_dma_data(cpu_dai, &aud2htx->dma_params_tx,
778a24c834SShengjiu Wang &aud2htx->dma_params_rx);
788a24c834SShengjiu Wang
798a24c834SShengjiu Wang return 0;
808a24c834SShengjiu Wang }
818a24c834SShengjiu Wang
82ac27ca16SKuninori Morimoto static const struct snd_soc_dai_ops fsl_aud2htx_dai_ops = {
838a24c834SShengjiu Wang .probe = fsl_aud2htx_dai_probe,
84ac27ca16SKuninori Morimoto .trigger = fsl_aud2htx_trigger,
85ac27ca16SKuninori Morimoto };
86ac27ca16SKuninori Morimoto
87ac27ca16SKuninori Morimoto static struct snd_soc_dai_driver fsl_aud2htx_dai = {
888a24c834SShengjiu Wang .playback = {
898a24c834SShengjiu Wang .stream_name = "CPU-Playback",
908a24c834SShengjiu Wang .channels_min = 1,
918a24c834SShengjiu Wang .channels_max = 8,
928a24c834SShengjiu Wang .rates = SNDRV_PCM_RATE_32000 |
938a24c834SShengjiu Wang SNDRV_PCM_RATE_44100 |
948a24c834SShengjiu Wang SNDRV_PCM_RATE_48000 |
958a24c834SShengjiu Wang SNDRV_PCM_RATE_88200 |
968a24c834SShengjiu Wang SNDRV_PCM_RATE_96000 |
978a24c834SShengjiu Wang SNDRV_PCM_RATE_176400 |
988a24c834SShengjiu Wang SNDRV_PCM_RATE_192000,
998a24c834SShengjiu Wang .formats = FSL_AUD2HTX_FORMATS,
1008a24c834SShengjiu Wang },
1018a24c834SShengjiu Wang .ops = &fsl_aud2htx_dai_ops,
1028a24c834SShengjiu Wang };
1038a24c834SShengjiu Wang
1048a24c834SShengjiu Wang static const struct snd_soc_component_driver fsl_aud2htx_component = {
1058a24c834SShengjiu Wang .name = "fsl-aud2htx",
1061e63fcc7SCharles Keepax .legacy_dai_naming = 1,
1078a24c834SShengjiu Wang };
1088a24c834SShengjiu Wang
1098a24c834SShengjiu Wang static const struct reg_default fsl_aud2htx_reg_defaults[] = {
1108a24c834SShengjiu Wang {AUD2HTX_CTRL, 0x00000000},
1118a24c834SShengjiu Wang {AUD2HTX_CTRL_EXT, 0x00000000},
1128a24c834SShengjiu Wang {AUD2HTX_WR, 0x00000000},
1138a24c834SShengjiu Wang {AUD2HTX_STATUS, 0x00000000},
1148a24c834SShengjiu Wang {AUD2HTX_IRQ_NOMASK, 0x00000000},
1158a24c834SShengjiu Wang {AUD2HTX_IRQ_MASKED, 0x00000000},
1168a24c834SShengjiu Wang {AUD2HTX_IRQ_MASK, 0x00000000},
1178a24c834SShengjiu Wang };
1188a24c834SShengjiu Wang
fsl_aud2htx_readable_reg(struct device * dev,unsigned int reg)1198a24c834SShengjiu Wang static bool fsl_aud2htx_readable_reg(struct device *dev, unsigned int reg)
1208a24c834SShengjiu Wang {
1218a24c834SShengjiu Wang switch (reg) {
1228a24c834SShengjiu Wang case AUD2HTX_CTRL:
1238a24c834SShengjiu Wang case AUD2HTX_CTRL_EXT:
1248a24c834SShengjiu Wang case AUD2HTX_STATUS:
1258a24c834SShengjiu Wang case AUD2HTX_IRQ_NOMASK:
1268a24c834SShengjiu Wang case AUD2HTX_IRQ_MASKED:
1278a24c834SShengjiu Wang case AUD2HTX_IRQ_MASK:
1288a24c834SShengjiu Wang return true;
1298a24c834SShengjiu Wang default:
1308a24c834SShengjiu Wang return false;
1318a24c834SShengjiu Wang }
1328a24c834SShengjiu Wang }
1338a24c834SShengjiu Wang
fsl_aud2htx_writeable_reg(struct device * dev,unsigned int reg)1348a24c834SShengjiu Wang static bool fsl_aud2htx_writeable_reg(struct device *dev, unsigned int reg)
1358a24c834SShengjiu Wang {
1368a24c834SShengjiu Wang switch (reg) {
1378a24c834SShengjiu Wang case AUD2HTX_CTRL:
1388a24c834SShengjiu Wang case AUD2HTX_CTRL_EXT:
1398a24c834SShengjiu Wang case AUD2HTX_WR:
1408a24c834SShengjiu Wang case AUD2HTX_IRQ_NOMASK:
1418a24c834SShengjiu Wang case AUD2HTX_IRQ_MASKED:
1428a24c834SShengjiu Wang case AUD2HTX_IRQ_MASK:
1438a24c834SShengjiu Wang return true;
1448a24c834SShengjiu Wang default:
1458a24c834SShengjiu Wang return false;
1468a24c834SShengjiu Wang }
1478a24c834SShengjiu Wang }
1488a24c834SShengjiu Wang
fsl_aud2htx_volatile_reg(struct device * dev,unsigned int reg)1498a24c834SShengjiu Wang static bool fsl_aud2htx_volatile_reg(struct device *dev, unsigned int reg)
1508a24c834SShengjiu Wang {
1518a24c834SShengjiu Wang switch (reg) {
1528a24c834SShengjiu Wang case AUD2HTX_STATUS:
1538a24c834SShengjiu Wang case AUD2HTX_IRQ_NOMASK:
1548a24c834SShengjiu Wang case AUD2HTX_IRQ_MASKED:
1558a24c834SShengjiu Wang return true;
1568a24c834SShengjiu Wang default:
1578a24c834SShengjiu Wang return false;
1588a24c834SShengjiu Wang }
1598a24c834SShengjiu Wang }
1608a24c834SShengjiu Wang
1618a24c834SShengjiu Wang static const struct regmap_config fsl_aud2htx_regmap_config = {
1628a24c834SShengjiu Wang .reg_bits = 32,
1638a24c834SShengjiu Wang .reg_stride = 4,
1648a24c834SShengjiu Wang .val_bits = 32,
1658a24c834SShengjiu Wang
1668a24c834SShengjiu Wang .max_register = AUD2HTX_IRQ_MASK,
1678a24c834SShengjiu Wang .reg_defaults = fsl_aud2htx_reg_defaults,
1688a24c834SShengjiu Wang .num_reg_defaults = ARRAY_SIZE(fsl_aud2htx_reg_defaults),
1698a24c834SShengjiu Wang .readable_reg = fsl_aud2htx_readable_reg,
1708a24c834SShengjiu Wang .volatile_reg = fsl_aud2htx_volatile_reg,
1718a24c834SShengjiu Wang .writeable_reg = fsl_aud2htx_writeable_reg,
1728a24c834SShengjiu Wang .cache_type = REGCACHE_RBTREE,
1738a24c834SShengjiu Wang };
1748a24c834SShengjiu Wang
1758a24c834SShengjiu Wang static const struct of_device_id fsl_aud2htx_dt_ids[] = {
1768a24c834SShengjiu Wang { .compatible = "fsl,imx8mp-aud2htx",},
1778a24c834SShengjiu Wang {}
1788a24c834SShengjiu Wang };
1798a24c834SShengjiu Wang MODULE_DEVICE_TABLE(of, fsl_aud2htx_dt_ids);
1808a24c834SShengjiu Wang
fsl_aud2htx_isr(int irq,void * dev_id)1818a24c834SShengjiu Wang static irqreturn_t fsl_aud2htx_isr(int irq, void *dev_id)
1828a24c834SShengjiu Wang {
1838a24c834SShengjiu Wang return IRQ_HANDLED;
1848a24c834SShengjiu Wang }
1858a24c834SShengjiu Wang
fsl_aud2htx_probe(struct platform_device * pdev)1868a24c834SShengjiu Wang static int fsl_aud2htx_probe(struct platform_device *pdev)
1878a24c834SShengjiu Wang {
1888a24c834SShengjiu Wang struct fsl_aud2htx *aud2htx;
1898a24c834SShengjiu Wang struct resource *res;
1908a24c834SShengjiu Wang void __iomem *regs;
1918a24c834SShengjiu Wang int ret, irq;
1928a24c834SShengjiu Wang
1938a24c834SShengjiu Wang aud2htx = devm_kzalloc(&pdev->dev, sizeof(*aud2htx), GFP_KERNEL);
1948a24c834SShengjiu Wang if (!aud2htx)
1958a24c834SShengjiu Wang return -ENOMEM;
1968a24c834SShengjiu Wang
1978a24c834SShengjiu Wang aud2htx->pdev = pdev;
1988a24c834SShengjiu Wang
19941e90cbbSYang Yingliang regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
200a93799d5SMuhammad Usama Anjum if (IS_ERR(regs))
2018a24c834SShengjiu Wang return PTR_ERR(regs);
2028a24c834SShengjiu Wang
2038a24c834SShengjiu Wang aud2htx->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
2048a24c834SShengjiu Wang &fsl_aud2htx_regmap_config);
2058a24c834SShengjiu Wang if (IS_ERR(aud2htx->regmap)) {
2068a24c834SShengjiu Wang dev_err(&pdev->dev, "failed to init regmap");
2078a24c834SShengjiu Wang return PTR_ERR(aud2htx->regmap);
2088a24c834SShengjiu Wang }
2098a24c834SShengjiu Wang
2108a24c834SShengjiu Wang irq = platform_get_irq(pdev, 0);
2111cc3245bSShengjiu Wang if (irq < 0)
2128a24c834SShengjiu Wang return irq;
2138a24c834SShengjiu Wang
2148a24c834SShengjiu Wang ret = devm_request_irq(&pdev->dev, irq, fsl_aud2htx_isr, 0,
2158a24c834SShengjiu Wang dev_name(&pdev->dev), aud2htx);
2168a24c834SShengjiu Wang if (ret) {
2178a24c834SShengjiu Wang dev_err(&pdev->dev, "failed to claim irq %u: %d\n", irq, ret);
2188a24c834SShengjiu Wang return ret;
2198a24c834SShengjiu Wang }
2208a24c834SShengjiu Wang
2218a24c834SShengjiu Wang aud2htx->bus_clk = devm_clk_get(&pdev->dev, "bus");
2228a24c834SShengjiu Wang if (IS_ERR(aud2htx->bus_clk)) {
2238a24c834SShengjiu Wang dev_err(&pdev->dev, "failed to get mem clock\n");
2248a24c834SShengjiu Wang return PTR_ERR(aud2htx->bus_clk);
2258a24c834SShengjiu Wang }
2268a24c834SShengjiu Wang
2278a24c834SShengjiu Wang aud2htx->dma_params_tx.chan_name = "tx";
2288a24c834SShengjiu Wang aud2htx->dma_params_tx.maxburst = AUD2HTX_MAXBURST;
2298a24c834SShengjiu Wang aud2htx->dma_params_tx.addr = res->start + AUD2HTX_WR;
2308a24c834SShengjiu Wang
2318a24c834SShengjiu Wang platform_set_drvdata(pdev, aud2htx);
2328a24c834SShengjiu Wang pm_runtime_enable(&pdev->dev);
2338a24c834SShengjiu Wang
2348a24c834SShengjiu Wang regcache_cache_only(aud2htx->regmap, true);
2358a24c834SShengjiu Wang
236ea532c29SShengjiu Wang /*
237ea532c29SShengjiu Wang * Register platform component before registering cpu dai for there
238ea532c29SShengjiu Wang * is not defer probe for platform component in snd_soc_add_pcm_runtime().
239ea532c29SShengjiu Wang */
240ea532c29SShengjiu Wang ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
241ea532c29SShengjiu Wang if (ret) {
242ea532c29SShengjiu Wang dev_err(&pdev->dev, "failed to pcm register\n");
243b1cd3fd4SShengjiu Wang pm_runtime_disable(&pdev->dev);
244ea532c29SShengjiu Wang return ret;
245ea532c29SShengjiu Wang }
246ea532c29SShengjiu Wang
2478a24c834SShengjiu Wang ret = devm_snd_soc_register_component(&pdev->dev,
2488a24c834SShengjiu Wang &fsl_aud2htx_component,
2498a24c834SShengjiu Wang &fsl_aud2htx_dai, 1);
2508a24c834SShengjiu Wang if (ret) {
2518a24c834SShengjiu Wang dev_err(&pdev->dev, "failed to register ASoC DAI\n");
252b1cd3fd4SShengjiu Wang pm_runtime_disable(&pdev->dev);
2538a24c834SShengjiu Wang return ret;
2548a24c834SShengjiu Wang }
2558a24c834SShengjiu Wang
2568a24c834SShengjiu Wang return ret;
2578a24c834SShengjiu Wang }
2588a24c834SShengjiu Wang
fsl_aud2htx_remove(struct platform_device * pdev)2592a41b192SUwe Kleine-König static void fsl_aud2htx_remove(struct platform_device *pdev)
2608a24c834SShengjiu Wang {
2618a24c834SShengjiu Wang pm_runtime_disable(&pdev->dev);
2628a24c834SShengjiu Wang }
2638a24c834SShengjiu Wang
fsl_aud2htx_runtime_suspend(struct device * dev)264560495c0SFabio Estevam static int fsl_aud2htx_runtime_suspend(struct device *dev)
2658a24c834SShengjiu Wang {
2668a24c834SShengjiu Wang struct fsl_aud2htx *aud2htx = dev_get_drvdata(dev);
2678a24c834SShengjiu Wang
2688a24c834SShengjiu Wang regcache_cache_only(aud2htx->regmap, true);
2698a24c834SShengjiu Wang clk_disable_unprepare(aud2htx->bus_clk);
2708a24c834SShengjiu Wang
2718a24c834SShengjiu Wang return 0;
2728a24c834SShengjiu Wang }
2738a24c834SShengjiu Wang
fsl_aud2htx_runtime_resume(struct device * dev)274560495c0SFabio Estevam static int fsl_aud2htx_runtime_resume(struct device *dev)
2758a24c834SShengjiu Wang {
2768a24c834SShengjiu Wang struct fsl_aud2htx *aud2htx = dev_get_drvdata(dev);
2778a24c834SShengjiu Wang int ret;
2788a24c834SShengjiu Wang
2798a24c834SShengjiu Wang ret = clk_prepare_enable(aud2htx->bus_clk);
2808a24c834SShengjiu Wang if (ret)
2818a24c834SShengjiu Wang return ret;
2828a24c834SShengjiu Wang
2838a24c834SShengjiu Wang regcache_cache_only(aud2htx->regmap, false);
2848a24c834SShengjiu Wang regcache_mark_dirty(aud2htx->regmap);
2858a24c834SShengjiu Wang regcache_sync(aud2htx->regmap);
2868a24c834SShengjiu Wang
2878a24c834SShengjiu Wang return 0;
2888a24c834SShengjiu Wang }
2898a24c834SShengjiu Wang
2908a24c834SShengjiu Wang static const struct dev_pm_ops fsl_aud2htx_pm_ops = {
291560495c0SFabio Estevam RUNTIME_PM_OPS(fsl_aud2htx_runtime_suspend, fsl_aud2htx_runtime_resume,
2928a24c834SShengjiu Wang NULL)
2938a24c834SShengjiu Wang SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
2948a24c834SShengjiu Wang pm_runtime_force_resume)
2958a24c834SShengjiu Wang };
2968a24c834SShengjiu Wang
2978a24c834SShengjiu Wang static struct platform_driver fsl_aud2htx_driver = {
2988a24c834SShengjiu Wang .probe = fsl_aud2htx_probe,
299*130af75bSUwe Kleine-König .remove = fsl_aud2htx_remove,
3008a24c834SShengjiu Wang .driver = {
3018a24c834SShengjiu Wang .name = "fsl-aud2htx",
302560495c0SFabio Estevam .pm = pm_ptr(&fsl_aud2htx_pm_ops),
3038a24c834SShengjiu Wang .of_match_table = fsl_aud2htx_dt_ids,
3048a24c834SShengjiu Wang },
3058a24c834SShengjiu Wang };
3068a24c834SShengjiu Wang module_platform_driver(fsl_aud2htx_driver);
3078a24c834SShengjiu Wang
3088a24c834SShengjiu Wang MODULE_AUTHOR("Shengjiu Wang <Shengjiu.Wang@nxp.com>");
3098a24c834SShengjiu Wang MODULE_DESCRIPTION("NXP AUD2HTX driver");
3108a24c834SShengjiu Wang MODULE_LICENSE("GPL v2");
311