1 // SPDX-License-Identifier: GPL-2.0-or-later 2 // 3 // Author: Kevin Wells <kevin.wells@nxp.com> 4 // 5 // Copyright (C) 2008 NXP Semiconductors 6 // Copyright 2023 Timesys Corporation <piotr.wojtaszczyk@timesys.com> 7 8 #include <linux/init.h> 9 #include <linux/module.h> 10 #include <linux/interrupt.h> 11 #include <linux/device.h> 12 #include <linux/delay.h> 13 #include <linux/clk.h> 14 #include <linux/io.h> 15 16 #include <sound/core.h> 17 #include <sound/pcm.h> 18 #include <sound/pcm_params.h> 19 #include <sound/dmaengine_pcm.h> 20 #include <sound/initval.h> 21 #include <sound/soc.h> 22 23 #include "lpc3xxx-i2s.h" 24 25 #define I2S_PLAYBACK_FLAG 0x1 26 #define I2S_CAPTURE_FLAG 0x2 27 28 #define LPC3XXX_I2S_RATES ( \ 29 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \ 30 SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ 31 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000) 32 33 #define LPC3XXX_I2S_FORMATS ( \ 34 SNDRV_PCM_FMTBIT_S8 | \ 35 SNDRV_PCM_FMTBIT_S16_LE | \ 36 SNDRV_PCM_FMTBIT_S32_LE) 37 38 static void __lpc3xxx_find_clkdiv(u32 *clkx, u32 *clky, int freq, int xbytes, u32 clkrate) 39 { 40 u32 i2srate; 41 u32 idxx, idyy; 42 u32 savedbitclkrate, diff, trate, baseclk; 43 44 /* Adjust rate for sample size (bits) and 2 channels and offset for 45 * divider in clock output 46 */ 47 i2srate = (freq / 100) * 2 * (8 * xbytes); 48 i2srate = i2srate << 1; 49 clkrate = clkrate / 100; 50 baseclk = clkrate; 51 *clkx = 1; 52 *clky = 1; 53 54 /* Find the best divider */ 55 *clkx = *clky = 0; 56 savedbitclkrate = 0; 57 diff = ~0; 58 for (idxx = 1; idxx < 0xFF; idxx++) { 59 for (idyy = 1; idyy < 0xFF; idyy++) { 60 trate = (baseclk * idxx) / idyy; 61 if (abs(trate - i2srate) < diff) { 62 diff = abs(trate - i2srate); 63 savedbitclkrate = trate; 64 *clkx = idxx; 65 *clky = idyy; 66 } 67 } 68 } 69 } 70 71 static int lpc3xxx_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) 72 { 73 struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai); 74 struct device *dev = i2s_info_p->dev; 75 u32 flag; 76 int ret = 0; 77 78 guard(mutex)(&i2s_info_p->lock); 79 80 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 81 flag = I2S_PLAYBACK_FLAG; 82 else 83 flag = I2S_CAPTURE_FLAG; 84 85 if (flag & i2s_info_p->streams_in_use) { 86 dev_warn(dev, "I2S channel is busy\n"); 87 ret = -EBUSY; 88 return ret; 89 } 90 91 if (i2s_info_p->streams_in_use == 0) { 92 ret = clk_prepare_enable(i2s_info_p->clk); 93 if (ret) { 94 dev_err(dev, "Can't enable clock, err=%d\n", ret); 95 return ret; 96 } 97 } 98 99 i2s_info_p->streams_in_use |= flag; 100 return 0; 101 } 102 103 static void lpc3xxx_i2s_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) 104 { 105 struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai); 106 struct regmap *regs = i2s_info_p->regs; 107 const u32 stop_bits = (LPC3XXX_I2S_RESET | LPC3XXX_I2S_STOP); 108 u32 flag; 109 110 guard(mutex)(&i2s_info_p->lock); 111 112 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 113 flag = I2S_PLAYBACK_FLAG; 114 regmap_write(regs, LPC3XXX_REG_I2S_TX_RATE, 0); 115 regmap_update_bits(regs, LPC3XXX_REG_I2S_DAO, stop_bits, stop_bits); 116 } else { 117 flag = I2S_CAPTURE_FLAG; 118 regmap_write(regs, LPC3XXX_REG_I2S_RX_RATE, 0); 119 regmap_update_bits(regs, LPC3XXX_REG_I2S_DAI, stop_bits, stop_bits); 120 } 121 i2s_info_p->streams_in_use &= ~flag; 122 123 if (i2s_info_p->streams_in_use == 0) 124 clk_disable_unprepare(i2s_info_p->clk); 125 } 126 127 static int lpc3xxx_i2s_set_dai_sysclk(struct snd_soc_dai *cpu_dai, 128 int clk_id, unsigned int freq, int dir) 129 { 130 struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai); 131 132 /* Will use in HW params later */ 133 i2s_info_p->freq = freq; 134 135 return 0; 136 } 137 138 static int lpc3xxx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) 139 { 140 struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai); 141 struct device *dev = i2s_info_p->dev; 142 143 if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_I2S) { 144 dev_warn(dev, "unsupported bus format %d\n", fmt); 145 return -EINVAL; 146 } 147 148 if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_BP_FP) { 149 dev_warn(dev, "unsupported clock provider %d\n", fmt); 150 return -EINVAL; 151 } 152 153 return 0; 154 } 155 156 static int lpc3xxx_i2s_hw_params(struct snd_pcm_substream *substream, 157 struct snd_pcm_hw_params *params, 158 struct snd_soc_dai *cpu_dai) 159 { 160 struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai); 161 struct device *dev = i2s_info_p->dev; 162 struct regmap *regs = i2s_info_p->regs; 163 int xfersize; 164 u32 tmp, clkx, clky; 165 166 tmp = LPC3XXX_I2S_RESET | LPC3XXX_I2S_STOP; 167 switch (params_format(params)) { 168 case SNDRV_PCM_FORMAT_S8: 169 tmp |= LPC3XXX_I2S_WW8 | LPC3XXX_I2S_WS_HP(LPC3XXX_I2S_WW8_HP); 170 xfersize = 1; 171 break; 172 173 case SNDRV_PCM_FORMAT_S16_LE: 174 tmp |= LPC3XXX_I2S_WW16 | LPC3XXX_I2S_WS_HP(LPC3XXX_I2S_WW16_HP); 175 xfersize = 2; 176 break; 177 178 case SNDRV_PCM_FORMAT_S32_LE: 179 tmp |= LPC3XXX_I2S_WW32 | LPC3XXX_I2S_WS_HP(LPC3XXX_I2S_WW32_HP); 180 xfersize = 4; 181 break; 182 183 default: 184 dev_warn(dev, "Unsupported audio data format %d\n", params_format(params)); 185 return -EINVAL; 186 } 187 188 if (params_channels(params) == 1) 189 tmp |= LPC3XXX_I2S_MONO; 190 191 __lpc3xxx_find_clkdiv(&clkx, &clky, i2s_info_p->freq, xfersize, i2s_info_p->clkrate); 192 193 dev_dbg(dev, "Stream : %s\n", 194 substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? "playback" : "capture"); 195 dev_dbg(dev, "Desired clock rate : %d\n", i2s_info_p->freq); 196 dev_dbg(dev, "Base clock rate : %d\n", i2s_info_p->clkrate); 197 dev_dbg(dev, "Transfer size (bytes) : %d\n", xfersize); 198 dev_dbg(dev, "Clock divider (x) : %d\n", clkx); 199 dev_dbg(dev, "Clock divider (y) : %d\n", clky); 200 dev_dbg(dev, "Channels : %d\n", params_channels(params)); 201 dev_dbg(dev, "Data format : %s\n", "I2S"); 202 203 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 204 regmap_write(regs, LPC3XXX_REG_I2S_DMA1, 205 LPC3XXX_I2S_DMA1_TX_EN | LPC3XXX_I2S_DMA0_TX_DEPTH(4)); 206 regmap_write(regs, LPC3XXX_REG_I2S_TX_RATE, (clkx << 8) | clky); 207 regmap_write(regs, LPC3XXX_REG_I2S_DAO, tmp); 208 } else { 209 regmap_write(regs, LPC3XXX_REG_I2S_DMA0, 210 LPC3XXX_I2S_DMA0_RX_EN | LPC3XXX_I2S_DMA1_RX_DEPTH(4)); 211 regmap_write(regs, LPC3XXX_REG_I2S_RX_RATE, (clkx << 8) | clky); 212 regmap_write(regs, LPC3XXX_REG_I2S_DAI, tmp); 213 } 214 215 return 0; 216 } 217 218 static int lpc3xxx_i2s_trigger(struct snd_pcm_substream *substream, int cmd, 219 struct snd_soc_dai *cpu_dai) 220 { 221 struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai); 222 struct regmap *regs = i2s_info_p->regs; 223 int ret = 0; 224 225 switch (cmd) { 226 case SNDRV_PCM_TRIGGER_STOP: 227 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 228 case SNDRV_PCM_TRIGGER_SUSPEND: 229 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 230 regmap_update_bits(regs, LPC3XXX_REG_I2S_DAO, 231 LPC3XXX_I2S_STOP, LPC3XXX_I2S_STOP); 232 else 233 regmap_update_bits(regs, LPC3XXX_REG_I2S_DAI, 234 LPC3XXX_I2S_STOP, LPC3XXX_I2S_STOP); 235 break; 236 237 case SNDRV_PCM_TRIGGER_START: 238 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 239 case SNDRV_PCM_TRIGGER_RESUME: 240 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 241 regmap_update_bits(regs, LPC3XXX_REG_I2S_DAO, 242 (LPC3XXX_I2S_RESET | LPC3XXX_I2S_STOP), 0); 243 else 244 regmap_update_bits(regs, LPC3XXX_REG_I2S_DAI, 245 (LPC3XXX_I2S_RESET | LPC3XXX_I2S_STOP), 0); 246 break; 247 default: 248 ret = -EINVAL; 249 } 250 251 return ret; 252 } 253 254 static int lpc3xxx_i2s_dai_probe(struct snd_soc_dai *dai) 255 { 256 struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(dai); 257 258 snd_soc_dai_init_dma_data(dai, &i2s_info_p->playback_dma_config, 259 &i2s_info_p->capture_dma_config); 260 return 0; 261 } 262 263 const struct snd_soc_dai_ops lpc3xxx_i2s_dai_ops = { 264 .probe = lpc3xxx_i2s_dai_probe, 265 .startup = lpc3xxx_i2s_startup, 266 .shutdown = lpc3xxx_i2s_shutdown, 267 .trigger = lpc3xxx_i2s_trigger, 268 .hw_params = lpc3xxx_i2s_hw_params, 269 .set_sysclk = lpc3xxx_i2s_set_dai_sysclk, 270 .set_fmt = lpc3xxx_i2s_set_dai_fmt, 271 }; 272 273 struct snd_soc_dai_driver lpc3xxx_i2s_dai_driver = { 274 .playback = { 275 .channels_min = 1, 276 .channels_max = 2, 277 .rates = LPC3XXX_I2S_RATES, 278 .formats = LPC3XXX_I2S_FORMATS, 279 }, 280 .capture = { 281 .channels_min = 1, 282 .channels_max = 2, 283 .rates = LPC3XXX_I2S_RATES, 284 .formats = LPC3XXX_I2S_FORMATS, 285 }, 286 .ops = &lpc3xxx_i2s_dai_ops, 287 .symmetric_rate = 1, 288 .symmetric_channels = 1, 289 .symmetric_sample_bits = 1, 290 }; 291 292 static const struct snd_soc_component_driver lpc32xx_i2s_component = { 293 .name = "lpc32xx-i2s", 294 .legacy_dai_naming = 1, 295 }; 296 297 static const struct regmap_config lpc32xx_i2s_regconfig = { 298 .reg_bits = 32, 299 .reg_stride = 4, 300 .val_bits = 32, 301 .max_register = LPC3XXX_REG_I2S_RX_RATE, 302 }; 303 304 static int lpc32xx_i2s_probe(struct platform_device *pdev) 305 { 306 struct device *dev = &pdev->dev; 307 struct lpc3xxx_i2s_info *i2s_info_p; 308 struct resource *res; 309 void __iomem *iomem; 310 int ret; 311 312 i2s_info_p = devm_kzalloc(dev, sizeof(*i2s_info_p), GFP_KERNEL); 313 if (!i2s_info_p) 314 return -ENOMEM; 315 316 platform_set_drvdata(pdev, i2s_info_p); 317 i2s_info_p->dev = dev; 318 319 iomem = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 320 if (IS_ERR(iomem)) 321 return dev_err_probe(dev, PTR_ERR(iomem), "Can't map registers\n"); 322 323 i2s_info_p->regs = devm_regmap_init_mmio(dev, iomem, &lpc32xx_i2s_regconfig); 324 if (IS_ERR(i2s_info_p->regs)) 325 return dev_err_probe(dev, PTR_ERR(i2s_info_p->regs), 326 "failed to init register map: %pe\n", i2s_info_p->regs); 327 328 i2s_info_p->clk = devm_clk_get(dev, NULL); 329 if (IS_ERR(i2s_info_p->clk)) 330 return dev_err_probe(dev, PTR_ERR(i2s_info_p->clk), "Can't get clock\n"); 331 332 i2s_info_p->clkrate = clk_get_rate(i2s_info_p->clk); 333 if (i2s_info_p->clkrate == 0) 334 return dev_err_probe(dev, -EINVAL, "Invalid returned clock rate\n"); 335 336 mutex_init(&i2s_info_p->lock); 337 338 ret = devm_snd_soc_register_component(dev, &lpc32xx_i2s_component, 339 &lpc3xxx_i2s_dai_driver, 1); 340 if (ret) 341 return dev_err_probe(dev, ret, "Can't register cpu_dai component\n"); 342 343 i2s_info_p->playback_dma_config.addr = (dma_addr_t)(res->start + LPC3XXX_REG_I2S_TX_FIFO); 344 i2s_info_p->playback_dma_config.maxburst = 4; 345 346 i2s_info_p->capture_dma_config.addr = (dma_addr_t)(res->start + LPC3XXX_REG_I2S_RX_FIFO); 347 i2s_info_p->capture_dma_config.maxburst = 4; 348 349 ret = lpc3xxx_pcm_register(pdev); 350 if (ret) 351 return dev_err_probe(dev, ret, "Can't register pcm component\n"); 352 353 return 0; 354 } 355 356 static const struct of_device_id lpc32xx_i2s_match[] = { 357 { .compatible = "nxp,lpc3220-i2s" }, 358 {}, 359 }; 360 MODULE_DEVICE_TABLE(of, lpc32xx_i2s_match); 361 362 static struct platform_driver lpc32xx_i2s_driver = { 363 .probe = lpc32xx_i2s_probe, 364 .driver = { 365 .name = "lpc3xxx-i2s", 366 .of_match_table = lpc32xx_i2s_match, 367 }, 368 }; 369 370 module_platform_driver(lpc32xx_i2s_driver); 371 372 MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com>"); 373 MODULE_AUTHOR("Piotr Wojtaszczyk <piotr.wojtaszczyk@timesys.com>"); 374 MODULE_DESCRIPTION("ASoC LPC3XXX I2S interface"); 375 MODULE_LICENSE("GPL"); 376
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.