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