1 // SPDX-License-Identifier: GPL-2.0+ 2 // Copyright 2020 NXP 3 4 #include <linux/clk.h> 5 #include <linux/clk-provider.h> 6 #include <linux/delay.h> 7 #include <linux/dmaengine.h> 8 #include <linux/module.h> 9 #include <linux/of_device.h> 10 #include <linux/of_address.h> 11 #include <linux/pm_runtime.h> 12 #include <linux/regmap.h> 13 #include <linux/slab.h> 14 #include <linux/time.h> 15 #include <linux/pm_qos.h> 16 #include <sound/core.h> 17 #include <sound/dmaengine_pcm.h> 18 #include <sound/pcm_params.h> 19 #include <linux/dma-mapping.h> 20 21 #include "fsl_aud2htx.h" 22 #include "imx-pcm.h" 23 24 static int fsl_aud2htx_trigger(struct snd_pcm_substream *substream, int cmd, 25 struct snd_soc_dai *dai) 26 { 27 struct fsl_aud2htx *aud2htx = snd_soc_dai_get_drvdata(dai); 28 29 switch (cmd) { 30 case SNDRV_PCM_TRIGGER_START: 31 case SNDRV_PCM_TRIGGER_RESUME: 32 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 33 regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL, 34 AUD2HTX_CTRL_EN, AUD2HTX_CTRL_EN); 35 regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT, 36 AUD2HTX_CTRE_DE, AUD2HTX_CTRE_DE); 37 break; 38 case SNDRV_PCM_TRIGGER_SUSPEND: 39 case SNDRV_PCM_TRIGGER_STOP: 40 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 41 regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT, 42 AUD2HTX_CTRE_DE, 0); 43 regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL, 44 AUD2HTX_CTRL_EN, 0); 45 break; 46 default: 47 return -EINVAL; 48 } 49 return 0; 50 } 51 52 static int fsl_aud2htx_dai_probe(struct snd_soc_dai *cpu_dai) 53 { 54 struct fsl_aud2htx *aud2htx = dev_get_drvdata(cpu_dai->dev); 55 56 /* DMA request when number of entries < WTMK_LOW */ 57 regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT, 58 AUD2HTX_CTRE_DT_MASK, 0); 59 60 /* Disable interrupts*/ 61 regmap_update_bits(aud2htx->regmap, AUD2HTX_IRQ_MASK, 62 AUD2HTX_WM_HIGH_IRQ_MASK | 63 AUD2HTX_WM_LOW_IRQ_MASK | 64 AUD2HTX_OVF_MASK, 65 AUD2HTX_WM_HIGH_IRQ_MASK | 66 AUD2HTX_WM_LOW_IRQ_MASK | 67 AUD2HTX_OVF_MASK); 68 69 /* Configure watermark */ 70 regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT, 71 AUD2HTX_CTRE_WL_MASK, 72 AUD2HTX_WTMK_LOW << AUD2HTX_CTRE_WL_SHIFT); 73 regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT, 74 AUD2HTX_CTRE_WH_MASK, 75 AUD2HTX_WTMK_HIGH << AUD2HTX_CTRE_WH_SHIFT); 76 77 snd_soc_dai_init_dma_data(cpu_dai, &aud2htx->dma_params_tx, 78 &aud2htx->dma_params_rx); 79 80 return 0; 81 } 82 83 static const struct snd_soc_dai_ops fsl_aud2htx_dai_ops = { 84 .probe = fsl_aud2htx_dai_probe, 85 .trigger = fsl_aud2htx_trigger, 86 }; 87 88 static struct snd_soc_dai_driver fsl_aud2htx_dai = { 89 .playback = { 90 .stream_name = "CPU-Playback", 91 .channels_min = 1, 92 .channels_max = 8, 93 .rates = SNDRV_PCM_RATE_32000 | 94 SNDRV_PCM_RATE_44100 | 95 SNDRV_PCM_RATE_48000 | 96 SNDRV_PCM_RATE_88200 | 97 SNDRV_PCM_RATE_96000 | 98 SNDRV_PCM_RATE_176400 | 99 SNDRV_PCM_RATE_192000, 100 .formats = FSL_AUD2HTX_FORMATS, 101 }, 102 .ops = &fsl_aud2htx_dai_ops, 103 }; 104 105 static const struct snd_soc_component_driver fsl_aud2htx_component = { 106 .name = "fsl-aud2htx", 107 .legacy_dai_naming = 1, 108 }; 109 110 static const struct reg_default fsl_aud2htx_reg_defaults[] = { 111 {AUD2HTX_CTRL, 0x00000000}, 112 {AUD2HTX_CTRL_EXT, 0x00000000}, 113 {AUD2HTX_WR, 0x00000000}, 114 {AUD2HTX_STATUS, 0x00000000}, 115 {AUD2HTX_IRQ_NOMASK, 0x00000000}, 116 {AUD2HTX_IRQ_MASKED, 0x00000000}, 117 {AUD2HTX_IRQ_MASK, 0x00000000}, 118 }; 119 120 static bool fsl_aud2htx_readable_reg(struct device *dev, unsigned int reg) 121 { 122 switch (reg) { 123 case AUD2HTX_CTRL: 124 case AUD2HTX_CTRL_EXT: 125 case AUD2HTX_STATUS: 126 case AUD2HTX_IRQ_NOMASK: 127 case AUD2HTX_IRQ_MASKED: 128 case AUD2HTX_IRQ_MASK: 129 return true; 130 default: 131 return false; 132 } 133 } 134 135 static bool fsl_aud2htx_writeable_reg(struct device *dev, unsigned int reg) 136 { 137 switch (reg) { 138 case AUD2HTX_CTRL: 139 case AUD2HTX_CTRL_EXT: 140 case AUD2HTX_WR: 141 case AUD2HTX_IRQ_NOMASK: 142 case AUD2HTX_IRQ_MASKED: 143 case AUD2HTX_IRQ_MASK: 144 return true; 145 default: 146 return false; 147 } 148 } 149 150 static bool fsl_aud2htx_volatile_reg(struct device *dev, unsigned int reg) 151 { 152 switch (reg) { 153 case AUD2HTX_STATUS: 154 case AUD2HTX_IRQ_NOMASK: 155 case AUD2HTX_IRQ_MASKED: 156 return true; 157 default: 158 return false; 159 } 160 } 161 162 static const struct regmap_config fsl_aud2htx_regmap_config = { 163 .reg_bits = 32, 164 .reg_stride = 4, 165 .val_bits = 32, 166 167 .max_register = AUD2HTX_IRQ_MASK, 168 .reg_defaults = fsl_aud2htx_reg_defaults, 169 .num_reg_defaults = ARRAY_SIZE(fsl_aud2htx_reg_defaults), 170 .readable_reg = fsl_aud2htx_readable_reg, 171 .volatile_reg = fsl_aud2htx_volatile_reg, 172 .writeable_reg = fsl_aud2htx_writeable_reg, 173 .cache_type = REGCACHE_RBTREE, 174 }; 175 176 static const struct of_device_id fsl_aud2htx_dt_ids[] = { 177 { .compatible = "fsl,imx8mp-aud2htx",}, 178 {} 179 }; 180 MODULE_DEVICE_TABLE(of, fsl_aud2htx_dt_ids); 181 182 static irqreturn_t fsl_aud2htx_isr(int irq, void *dev_id) 183 { 184 return IRQ_HANDLED; 185 } 186 187 static int fsl_aud2htx_probe(struct platform_device *pdev) 188 { 189 struct fsl_aud2htx *aud2htx; 190 struct resource *res; 191 void __iomem *regs; 192 int ret, irq; 193 194 aud2htx = devm_kzalloc(&pdev->dev, sizeof(*aud2htx), GFP_KERNEL); 195 if (!aud2htx) 196 return -ENOMEM; 197 198 aud2htx->pdev = pdev; 199 200 regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 201 if (IS_ERR(regs)) 202 return PTR_ERR(regs); 203 204 aud2htx->regmap = devm_regmap_init_mmio(&pdev->dev, regs, 205 &fsl_aud2htx_regmap_config); 206 if (IS_ERR(aud2htx->regmap)) { 207 dev_err(&pdev->dev, "failed to init regmap"); 208 return PTR_ERR(aud2htx->regmap); 209 } 210 211 irq = platform_get_irq(pdev, 0); 212 if (irq < 0) 213 return irq; 214 215 ret = devm_request_irq(&pdev->dev, irq, fsl_aud2htx_isr, 0, 216 dev_name(&pdev->dev), aud2htx); 217 if (ret) { 218 dev_err(&pdev->dev, "failed to claim irq %u: %d\n", irq, ret); 219 return ret; 220 } 221 222 aud2htx->bus_clk = devm_clk_get(&pdev->dev, "bus"); 223 if (IS_ERR(aud2htx->bus_clk)) { 224 dev_err(&pdev->dev, "failed to get mem clock\n"); 225 return PTR_ERR(aud2htx->bus_clk); 226 } 227 228 aud2htx->dma_params_tx.chan_name = "tx"; 229 aud2htx->dma_params_tx.maxburst = AUD2HTX_MAXBURST; 230 aud2htx->dma_params_tx.addr = res->start + AUD2HTX_WR; 231 232 platform_set_drvdata(pdev, aud2htx); 233 pm_runtime_enable(&pdev->dev); 234 235 regcache_cache_only(aud2htx->regmap, true); 236 237 /* 238 * Register platform component before registering cpu dai for there 239 * is not defer probe for platform component in snd_soc_add_pcm_runtime(). 240 */ 241 ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); 242 if (ret) { 243 dev_err(&pdev->dev, "failed to pcm register\n"); 244 pm_runtime_disable(&pdev->dev); 245 return ret; 246 } 247 248 ret = devm_snd_soc_register_component(&pdev->dev, 249 &fsl_aud2htx_component, 250 &fsl_aud2htx_dai, 1); 251 if (ret) { 252 dev_err(&pdev->dev, "failed to register ASoC DAI\n"); 253 pm_runtime_disable(&pdev->dev); 254 return ret; 255 } 256 257 return ret; 258 } 259 260 static void fsl_aud2htx_remove(struct platform_device *pdev) 261 { 262 pm_runtime_disable(&pdev->dev); 263 } 264 265 static int __maybe_unused fsl_aud2htx_runtime_suspend(struct device *dev) 266 { 267 struct fsl_aud2htx *aud2htx = dev_get_drvdata(dev); 268 269 regcache_cache_only(aud2htx->regmap, true); 270 clk_disable_unprepare(aud2htx->bus_clk); 271 272 return 0; 273 } 274 275 static int __maybe_unused fsl_aud2htx_runtime_resume(struct device *dev) 276 { 277 struct fsl_aud2htx *aud2htx = dev_get_drvdata(dev); 278 int ret; 279 280 ret = clk_prepare_enable(aud2htx->bus_clk); 281 if (ret) 282 return ret; 283 284 regcache_cache_only(aud2htx->regmap, false); 285 regcache_mark_dirty(aud2htx->regmap); 286 regcache_sync(aud2htx->regmap); 287 288 return 0; 289 } 290 291 static const struct dev_pm_ops fsl_aud2htx_pm_ops = { 292 SET_RUNTIME_PM_OPS(fsl_aud2htx_runtime_suspend, 293 fsl_aud2htx_runtime_resume, 294 NULL) 295 SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 296 pm_runtime_force_resume) 297 }; 298 299 static struct platform_driver fsl_aud2htx_driver = { 300 .probe = fsl_aud2htx_probe, 301 .remove_new = fsl_aud2htx_remove, 302 .driver = { 303 .name = "fsl-aud2htx", 304 .pm = &fsl_aud2htx_pm_ops, 305 .of_match_table = fsl_aud2htx_dt_ids, 306 }, 307 }; 308 module_platform_driver(fsl_aud2htx_driver); 309 310 MODULE_AUTHOR("Shengjiu Wang <Shengjiu.Wang@nxp.com>"); 311 MODULE_DESCRIPTION("NXP AUD2HTX driver"); 312 MODULE_LICENSE("GPL v2"); 313