0001
0002
0003
0004 #include <linux/clk.h>
0005 #include <linux/clk-provider.h>
0006 #include <linux/delay.h>
0007 #include <linux/dmaengine.h>
0008 #include <linux/module.h>
0009 #include <linux/of_device.h>
0010 #include <linux/of_address.h>
0011 #include <linux/pm_runtime.h>
0012 #include <linux/regmap.h>
0013 #include <linux/slab.h>
0014 #include <linux/time.h>
0015 #include <linux/pm_qos.h>
0016 #include <sound/core.h>
0017 #include <sound/dmaengine_pcm.h>
0018 #include <sound/pcm_params.h>
0019 #include <linux/dma-mapping.h>
0020
0021 #include "fsl_aud2htx.h"
0022 #include "imx-pcm.h"
0023
0024 static int fsl_aud2htx_trigger(struct snd_pcm_substream *substream, int cmd,
0025 struct snd_soc_dai *dai)
0026 {
0027 struct fsl_aud2htx *aud2htx = snd_soc_dai_get_drvdata(dai);
0028
0029 switch (cmd) {
0030 case SNDRV_PCM_TRIGGER_START:
0031 case SNDRV_PCM_TRIGGER_RESUME:
0032 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
0033 regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL,
0034 AUD2HTX_CTRL_EN, AUD2HTX_CTRL_EN);
0035 regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT,
0036 AUD2HTX_CTRE_DE, AUD2HTX_CTRE_DE);
0037 break;
0038 case SNDRV_PCM_TRIGGER_SUSPEND:
0039 case SNDRV_PCM_TRIGGER_STOP:
0040 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
0041 regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT,
0042 AUD2HTX_CTRE_DE, 0);
0043 regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL,
0044 AUD2HTX_CTRL_EN, 0);
0045 break;
0046 default:
0047 return -EINVAL;
0048 }
0049 return 0;
0050 }
0051
0052 static const struct snd_soc_dai_ops fsl_aud2htx_dai_ops = {
0053 .trigger = fsl_aud2htx_trigger,
0054 };
0055
0056 static int fsl_aud2htx_dai_probe(struct snd_soc_dai *cpu_dai)
0057 {
0058 struct fsl_aud2htx *aud2htx = dev_get_drvdata(cpu_dai->dev);
0059
0060
0061 regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT,
0062 AUD2HTX_CTRE_DT_MASK, 0);
0063
0064
0065 regmap_update_bits(aud2htx->regmap, AUD2HTX_IRQ_MASK,
0066 AUD2HTX_WM_HIGH_IRQ_MASK |
0067 AUD2HTX_WM_LOW_IRQ_MASK |
0068 AUD2HTX_OVF_MASK,
0069 AUD2HTX_WM_HIGH_IRQ_MASK |
0070 AUD2HTX_WM_LOW_IRQ_MASK |
0071 AUD2HTX_OVF_MASK);
0072
0073
0074 regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT,
0075 AUD2HTX_CTRE_WL_MASK,
0076 AUD2HTX_WTMK_LOW << AUD2HTX_CTRE_WL_SHIFT);
0077 regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT,
0078 AUD2HTX_CTRE_WH_MASK,
0079 AUD2HTX_WTMK_HIGH << AUD2HTX_CTRE_WH_SHIFT);
0080
0081 snd_soc_dai_init_dma_data(cpu_dai, &aud2htx->dma_params_tx,
0082 &aud2htx->dma_params_rx);
0083
0084 return 0;
0085 }
0086
0087 static struct snd_soc_dai_driver fsl_aud2htx_dai = {
0088 .probe = fsl_aud2htx_dai_probe,
0089 .playback = {
0090 .stream_name = "CPU-Playback",
0091 .channels_min = 1,
0092 .channels_max = 8,
0093 .rates = SNDRV_PCM_RATE_32000 |
0094 SNDRV_PCM_RATE_44100 |
0095 SNDRV_PCM_RATE_48000 |
0096 SNDRV_PCM_RATE_88200 |
0097 SNDRV_PCM_RATE_96000 |
0098 SNDRV_PCM_RATE_176400 |
0099 SNDRV_PCM_RATE_192000,
0100 .formats = FSL_AUD2HTX_FORMATS,
0101 },
0102 .ops = &fsl_aud2htx_dai_ops,
0103 };
0104
0105 static const struct snd_soc_component_driver fsl_aud2htx_component = {
0106 .name = "fsl-aud2htx",
0107 .legacy_dai_naming = 1,
0108 };
0109
0110 static const struct reg_default fsl_aud2htx_reg_defaults[] = {
0111 {AUD2HTX_CTRL, 0x00000000},
0112 {AUD2HTX_CTRL_EXT, 0x00000000},
0113 {AUD2HTX_WR, 0x00000000},
0114 {AUD2HTX_STATUS, 0x00000000},
0115 {AUD2HTX_IRQ_NOMASK, 0x00000000},
0116 {AUD2HTX_IRQ_MASKED, 0x00000000},
0117 {AUD2HTX_IRQ_MASK, 0x00000000},
0118 };
0119
0120 static bool fsl_aud2htx_readable_reg(struct device *dev, unsigned int reg)
0121 {
0122 switch (reg) {
0123 case AUD2HTX_CTRL:
0124 case AUD2HTX_CTRL_EXT:
0125 case AUD2HTX_STATUS:
0126 case AUD2HTX_IRQ_NOMASK:
0127 case AUD2HTX_IRQ_MASKED:
0128 case AUD2HTX_IRQ_MASK:
0129 return true;
0130 default:
0131 return false;
0132 }
0133 }
0134
0135 static bool fsl_aud2htx_writeable_reg(struct device *dev, unsigned int reg)
0136 {
0137 switch (reg) {
0138 case AUD2HTX_CTRL:
0139 case AUD2HTX_CTRL_EXT:
0140 case AUD2HTX_WR:
0141 case AUD2HTX_IRQ_NOMASK:
0142 case AUD2HTX_IRQ_MASKED:
0143 case AUD2HTX_IRQ_MASK:
0144 return true;
0145 default:
0146 return false;
0147 }
0148 }
0149
0150 static bool fsl_aud2htx_volatile_reg(struct device *dev, unsigned int reg)
0151 {
0152 switch (reg) {
0153 case AUD2HTX_STATUS:
0154 case AUD2HTX_IRQ_NOMASK:
0155 case AUD2HTX_IRQ_MASKED:
0156 return true;
0157 default:
0158 return false;
0159 }
0160 }
0161
0162 static const struct regmap_config fsl_aud2htx_regmap_config = {
0163 .reg_bits = 32,
0164 .reg_stride = 4,
0165 .val_bits = 32,
0166
0167 .max_register = AUD2HTX_IRQ_MASK,
0168 .reg_defaults = fsl_aud2htx_reg_defaults,
0169 .num_reg_defaults = ARRAY_SIZE(fsl_aud2htx_reg_defaults),
0170 .readable_reg = fsl_aud2htx_readable_reg,
0171 .volatile_reg = fsl_aud2htx_volatile_reg,
0172 .writeable_reg = fsl_aud2htx_writeable_reg,
0173 .cache_type = REGCACHE_RBTREE,
0174 };
0175
0176 static const struct of_device_id fsl_aud2htx_dt_ids[] = {
0177 { .compatible = "fsl,imx8mp-aud2htx",},
0178 {}
0179 };
0180 MODULE_DEVICE_TABLE(of, fsl_aud2htx_dt_ids);
0181
0182 static irqreturn_t fsl_aud2htx_isr(int irq, void *dev_id)
0183 {
0184 return IRQ_HANDLED;
0185 }
0186
0187 static int fsl_aud2htx_probe(struct platform_device *pdev)
0188 {
0189 struct fsl_aud2htx *aud2htx;
0190 struct resource *res;
0191 void __iomem *regs;
0192 int ret, irq;
0193
0194 aud2htx = devm_kzalloc(&pdev->dev, sizeof(*aud2htx), GFP_KERNEL);
0195 if (!aud2htx)
0196 return -ENOMEM;
0197
0198 aud2htx->pdev = pdev;
0199
0200 regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
0201 if (IS_ERR(regs))
0202 return PTR_ERR(regs);
0203
0204 aud2htx->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
0205 &fsl_aud2htx_regmap_config);
0206 if (IS_ERR(aud2htx->regmap)) {
0207 dev_err(&pdev->dev, "failed to init regmap");
0208 return PTR_ERR(aud2htx->regmap);
0209 }
0210
0211 irq = platform_get_irq(pdev, 0);
0212 if (irq < 0)
0213 return irq;
0214
0215 ret = devm_request_irq(&pdev->dev, irq, fsl_aud2htx_isr, 0,
0216 dev_name(&pdev->dev), aud2htx);
0217 if (ret) {
0218 dev_err(&pdev->dev, "failed to claim irq %u: %d\n", irq, ret);
0219 return ret;
0220 }
0221
0222 aud2htx->bus_clk = devm_clk_get(&pdev->dev, "bus");
0223 if (IS_ERR(aud2htx->bus_clk)) {
0224 dev_err(&pdev->dev, "failed to get mem clock\n");
0225 return PTR_ERR(aud2htx->bus_clk);
0226 }
0227
0228 aud2htx->dma_params_tx.chan_name = "tx";
0229 aud2htx->dma_params_tx.maxburst = AUD2HTX_MAXBURST;
0230 aud2htx->dma_params_tx.addr = res->start + AUD2HTX_WR;
0231
0232 platform_set_drvdata(pdev, aud2htx);
0233 pm_runtime_enable(&pdev->dev);
0234
0235 regcache_cache_only(aud2htx->regmap, true);
0236
0237
0238
0239
0240
0241 ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
0242 if (ret) {
0243 dev_err(&pdev->dev, "failed to pcm register\n");
0244 pm_runtime_disable(&pdev->dev);
0245 return ret;
0246 }
0247
0248 ret = devm_snd_soc_register_component(&pdev->dev,
0249 &fsl_aud2htx_component,
0250 &fsl_aud2htx_dai, 1);
0251 if (ret) {
0252 dev_err(&pdev->dev, "failed to register ASoC DAI\n");
0253 pm_runtime_disable(&pdev->dev);
0254 return ret;
0255 }
0256
0257 return ret;
0258 }
0259
0260 static int fsl_aud2htx_remove(struct platform_device *pdev)
0261 {
0262 pm_runtime_disable(&pdev->dev);
0263
0264 return 0;
0265 }
0266
0267 static int __maybe_unused fsl_aud2htx_runtime_suspend(struct device *dev)
0268 {
0269 struct fsl_aud2htx *aud2htx = dev_get_drvdata(dev);
0270
0271 regcache_cache_only(aud2htx->regmap, true);
0272 clk_disable_unprepare(aud2htx->bus_clk);
0273
0274 return 0;
0275 }
0276
0277 static int __maybe_unused fsl_aud2htx_runtime_resume(struct device *dev)
0278 {
0279 struct fsl_aud2htx *aud2htx = dev_get_drvdata(dev);
0280 int ret;
0281
0282 ret = clk_prepare_enable(aud2htx->bus_clk);
0283 if (ret)
0284 return ret;
0285
0286 regcache_cache_only(aud2htx->regmap, false);
0287 regcache_mark_dirty(aud2htx->regmap);
0288 regcache_sync(aud2htx->regmap);
0289
0290 return 0;
0291 }
0292
0293 static const struct dev_pm_ops fsl_aud2htx_pm_ops = {
0294 SET_RUNTIME_PM_OPS(fsl_aud2htx_runtime_suspend,
0295 fsl_aud2htx_runtime_resume,
0296 NULL)
0297 SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
0298 pm_runtime_force_resume)
0299 };
0300
0301 static struct platform_driver fsl_aud2htx_driver = {
0302 .probe = fsl_aud2htx_probe,
0303 .remove = fsl_aud2htx_remove,
0304 .driver = {
0305 .name = "fsl-aud2htx",
0306 .pm = &fsl_aud2htx_pm_ops,
0307 .of_match_table = fsl_aud2htx_dt_ids,
0308 },
0309 };
0310 module_platform_driver(fsl_aud2htx_driver);
0311
0312 MODULE_AUTHOR("Shengjiu Wang <Shengjiu.Wang@nxp.com>");
0313 MODULE_DESCRIPTION("NXP AUD2HTX driver");
0314 MODULE_LICENSE("GPL v2");