xref: /linux/sound/soc/fsl/fsl_aud2htx.c (revision 3a39d672e7f48b8d6b91a09afa4b55352773b4b5)
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