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