1 /* 2 * kirkwood-i2s.c 3 * 4 * (c) 2010 Arnaud Patard <apatard@mandriva.com> 5 * (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org> 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the 9 * Free Software Foundation; either version 2 of the License, or (at your 10 * option) any later version. 11 */ 12 13 #include <linux/init.h> 14 #include <linux/module.h> 15 #include <linux/platform_device.h> 16 #include <linux/io.h> 17 #include <linux/slab.h> 18 #include <linux/mbus.h> 19 #include <linux/delay.h> 20 #include <linux/clk.h> 21 #include <sound/pcm.h> 22 #include <sound/pcm_params.h> 23 #include <sound/soc.h> 24 #include <linux/platform_data/asoc-kirkwood.h> 25 #include "kirkwood.h" 26 27 #define DRV_NAME "kirkwood-i2s" 28 29 #define KIRKWOOD_I2S_RATES \ 30 (SNDRV_PCM_RATE_44100 | \ 31 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000) 32 #define KIRKWOOD_I2S_FORMATS \ 33 (SNDRV_PCM_FMTBIT_S16_LE | \ 34 SNDRV_PCM_FMTBIT_S24_LE | \ 35 SNDRV_PCM_FMTBIT_S32_LE) 36 37 static int kirkwood_i2s_set_fmt(struct snd_soc_dai *cpu_dai, 38 unsigned int fmt) 39 { 40 struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(cpu_dai); 41 unsigned long mask; 42 unsigned long value; 43 44 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 45 case SND_SOC_DAIFMT_RIGHT_J: 46 mask = KIRKWOOD_I2S_CTL_RJ; 47 break; 48 case SND_SOC_DAIFMT_LEFT_J: 49 mask = KIRKWOOD_I2S_CTL_LJ; 50 break; 51 case SND_SOC_DAIFMT_I2S: 52 mask = KIRKWOOD_I2S_CTL_I2S; 53 break; 54 default: 55 return -EINVAL; 56 } 57 58 /* 59 * Set same format for playback and record 60 * This avoids some troubles. 61 */ 62 value = readl(priv->io+KIRKWOOD_I2S_PLAYCTL); 63 value &= ~KIRKWOOD_I2S_CTL_JUST_MASK; 64 value |= mask; 65 writel(value, priv->io+KIRKWOOD_I2S_PLAYCTL); 66 67 value = readl(priv->io+KIRKWOOD_I2S_RECCTL); 68 value &= ~KIRKWOOD_I2S_CTL_JUST_MASK; 69 value |= mask; 70 writel(value, priv->io+KIRKWOOD_I2S_RECCTL); 71 72 return 0; 73 } 74 75 static inline void kirkwood_set_dco(void __iomem *io, unsigned long rate) 76 { 77 unsigned long value; 78 79 value = KIRKWOOD_DCO_CTL_OFFSET_0; 80 switch (rate) { 81 default: 82 case 44100: 83 value |= KIRKWOOD_DCO_CTL_FREQ_11; 84 break; 85 case 48000: 86 value |= KIRKWOOD_DCO_CTL_FREQ_12; 87 break; 88 case 96000: 89 value |= KIRKWOOD_DCO_CTL_FREQ_24; 90 break; 91 } 92 writel(value, io + KIRKWOOD_DCO_CTL); 93 94 /* wait for dco locked */ 95 do { 96 cpu_relax(); 97 value = readl(io + KIRKWOOD_DCO_SPCR_STATUS); 98 value &= KIRKWOOD_DCO_SPCR_STATUS_DCO_LOCK; 99 } while (value == 0); 100 } 101 102 static int kirkwood_i2s_startup(struct snd_pcm_substream *substream, 103 struct snd_soc_dai *dai) 104 { 105 struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai); 106 107 snd_soc_dai_set_dma_data(dai, substream, priv); 108 return 0; 109 } 110 111 static int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream, 112 struct snd_pcm_hw_params *params, 113 struct snd_soc_dai *dai) 114 { 115 struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai); 116 unsigned int i2s_reg, reg; 117 unsigned long i2s_value, value; 118 119 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 120 i2s_reg = KIRKWOOD_I2S_PLAYCTL; 121 reg = KIRKWOOD_PLAYCTL; 122 } else { 123 i2s_reg = KIRKWOOD_I2S_RECCTL; 124 reg = KIRKWOOD_RECCTL; 125 } 126 127 /* set dco conf */ 128 kirkwood_set_dco(priv->io, params_rate(params)); 129 130 i2s_value = readl(priv->io+i2s_reg); 131 i2s_value &= ~KIRKWOOD_I2S_CTL_SIZE_MASK; 132 133 value = readl(priv->io+reg); 134 value &= ~KIRKWOOD_PLAYCTL_SIZE_MASK; 135 136 /* 137 * Size settings in play/rec i2s control regs and play/rec control 138 * regs must be the same. 139 */ 140 switch (params_format(params)) { 141 case SNDRV_PCM_FORMAT_S16_LE: 142 i2s_value |= KIRKWOOD_I2S_CTL_SIZE_16; 143 value |= KIRKWOOD_PLAYCTL_SIZE_16_C; 144 break; 145 /* 146 * doesn't work... S20_3LE != kirkwood 20bit format ? 147 * 148 case SNDRV_PCM_FORMAT_S20_3LE: 149 i2s_value |= KIRKWOOD_I2S_CTL_SIZE_20; 150 value |= KIRKWOOD_PLAYCTL_SIZE_20; 151 break; 152 */ 153 case SNDRV_PCM_FORMAT_S24_LE: 154 i2s_value |= KIRKWOOD_I2S_CTL_SIZE_24; 155 value |= KIRKWOOD_PLAYCTL_SIZE_24; 156 break; 157 case SNDRV_PCM_FORMAT_S32_LE: 158 i2s_value |= KIRKWOOD_I2S_CTL_SIZE_32; 159 value |= KIRKWOOD_PLAYCTL_SIZE_32; 160 break; 161 default: 162 return -EINVAL; 163 } 164 165 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 166 value &= ~KIRKWOOD_PLAYCTL_MONO_MASK; 167 if (params_channels(params) == 1) 168 value |= KIRKWOOD_PLAYCTL_MONO_BOTH; 169 else 170 value |= KIRKWOOD_PLAYCTL_MONO_OFF; 171 } 172 173 writel(i2s_value, priv->io+i2s_reg); 174 writel(value, priv->io+reg); 175 176 return 0; 177 } 178 179 static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream, 180 int cmd, struct snd_soc_dai *dai) 181 { 182 struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai); 183 uint32_t ctl, value; 184 185 ctl = readl(priv->io + KIRKWOOD_PLAYCTL); 186 if (ctl & KIRKWOOD_PLAYCTL_PAUSE) { 187 unsigned timeout = 5000; 188 /* 189 * The Armada510 spec says that if we enter pause mode, the 190 * busy bit must be read back as clear _twice_. Make sure 191 * we respect that otherwise we get DMA underruns. 192 */ 193 do { 194 value = ctl; 195 ctl = readl(priv->io + KIRKWOOD_PLAYCTL); 196 if (!((ctl | value) & KIRKWOOD_PLAYCTL_PLAY_BUSY)) 197 break; 198 udelay(1); 199 } while (timeout--); 200 201 if ((ctl | value) & KIRKWOOD_PLAYCTL_PLAY_BUSY) 202 dev_notice(dai->dev, "timed out waiting for busy to deassert: %08x\n", 203 ctl); 204 } 205 206 switch (cmd) { 207 case SNDRV_PCM_TRIGGER_START: 208 /* stop audio, enable interrupts */ 209 ctl |= KIRKWOOD_PLAYCTL_PAUSE; 210 writel(ctl, priv->io + KIRKWOOD_PLAYCTL); 211 212 value = readl(priv->io + KIRKWOOD_INT_MASK); 213 value |= KIRKWOOD_INT_CAUSE_PLAY_BYTES; 214 writel(value, priv->io + KIRKWOOD_INT_MASK); 215 216 /* configure audio & enable i2s playback */ 217 ctl &= ~KIRKWOOD_PLAYCTL_BURST_MASK; 218 ctl &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE 219 | KIRKWOOD_PLAYCTL_SPDIF_EN); 220 221 if (priv->burst == 32) 222 ctl |= KIRKWOOD_PLAYCTL_BURST_32; 223 else 224 ctl |= KIRKWOOD_PLAYCTL_BURST_128; 225 ctl |= KIRKWOOD_PLAYCTL_I2S_EN; 226 writel(ctl, priv->io + KIRKWOOD_PLAYCTL); 227 break; 228 229 case SNDRV_PCM_TRIGGER_STOP: 230 /* stop audio, disable interrupts */ 231 ctl |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE; 232 writel(ctl, priv->io + KIRKWOOD_PLAYCTL); 233 234 value = readl(priv->io + KIRKWOOD_INT_MASK); 235 value &= ~KIRKWOOD_INT_CAUSE_PLAY_BYTES; 236 writel(value, priv->io + KIRKWOOD_INT_MASK); 237 238 /* disable all playbacks */ 239 ctl &= ~(KIRKWOOD_PLAYCTL_I2S_EN | KIRKWOOD_PLAYCTL_SPDIF_EN); 240 writel(ctl, priv->io + KIRKWOOD_PLAYCTL); 241 break; 242 243 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 244 case SNDRV_PCM_TRIGGER_SUSPEND: 245 ctl |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE; 246 writel(ctl, priv->io + KIRKWOOD_PLAYCTL); 247 break; 248 249 case SNDRV_PCM_TRIGGER_RESUME: 250 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 251 ctl &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE); 252 writel(ctl, priv->io + KIRKWOOD_PLAYCTL); 253 break; 254 255 default: 256 return -EINVAL; 257 } 258 259 return 0; 260 } 261 262 static int kirkwood_i2s_rec_trigger(struct snd_pcm_substream *substream, 263 int cmd, struct snd_soc_dai *dai) 264 { 265 struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai); 266 unsigned long value; 267 268 value = readl(priv->io + KIRKWOOD_RECCTL); 269 270 switch (cmd) { 271 case SNDRV_PCM_TRIGGER_START: 272 /* stop audio, enable interrupts */ 273 value = readl(priv->io + KIRKWOOD_RECCTL); 274 value |= KIRKWOOD_RECCTL_PAUSE; 275 writel(value, priv->io + KIRKWOOD_RECCTL); 276 277 value = readl(priv->io + KIRKWOOD_INT_MASK); 278 value |= KIRKWOOD_INT_CAUSE_REC_BYTES; 279 writel(value, priv->io + KIRKWOOD_INT_MASK); 280 281 /* configure audio & enable i2s record */ 282 value = readl(priv->io + KIRKWOOD_RECCTL); 283 value &= ~KIRKWOOD_RECCTL_BURST_MASK; 284 value &= ~KIRKWOOD_RECCTL_MONO; 285 value &= ~(KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE 286 | KIRKWOOD_RECCTL_SPDIF_EN); 287 288 if (priv->burst == 32) 289 value |= KIRKWOOD_RECCTL_BURST_32; 290 else 291 value |= KIRKWOOD_RECCTL_BURST_128; 292 value |= KIRKWOOD_RECCTL_I2S_EN; 293 294 writel(value, priv->io + KIRKWOOD_RECCTL); 295 break; 296 297 case SNDRV_PCM_TRIGGER_STOP: 298 /* stop audio, disable interrupts */ 299 value = readl(priv->io + KIRKWOOD_RECCTL); 300 value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE; 301 writel(value, priv->io + KIRKWOOD_RECCTL); 302 303 value = readl(priv->io + KIRKWOOD_INT_MASK); 304 value &= ~KIRKWOOD_INT_CAUSE_REC_BYTES; 305 writel(value, priv->io + KIRKWOOD_INT_MASK); 306 307 /* disable all records */ 308 value = readl(priv->io + KIRKWOOD_RECCTL); 309 value &= ~(KIRKWOOD_RECCTL_I2S_EN | KIRKWOOD_RECCTL_SPDIF_EN); 310 writel(value, priv->io + KIRKWOOD_RECCTL); 311 break; 312 313 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 314 case SNDRV_PCM_TRIGGER_SUSPEND: 315 value = readl(priv->io + KIRKWOOD_RECCTL); 316 value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE; 317 writel(value, priv->io + KIRKWOOD_RECCTL); 318 break; 319 320 case SNDRV_PCM_TRIGGER_RESUME: 321 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 322 value = readl(priv->io + KIRKWOOD_RECCTL); 323 value &= ~(KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE); 324 writel(value, priv->io + KIRKWOOD_RECCTL); 325 break; 326 327 default: 328 return -EINVAL; 329 } 330 331 return 0; 332 } 333 334 static int kirkwood_i2s_trigger(struct snd_pcm_substream *substream, int cmd, 335 struct snd_soc_dai *dai) 336 { 337 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 338 return kirkwood_i2s_play_trigger(substream, cmd, dai); 339 else 340 return kirkwood_i2s_rec_trigger(substream, cmd, dai); 341 342 return 0; 343 } 344 345 static int kirkwood_i2s_probe(struct snd_soc_dai *dai) 346 { 347 struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai); 348 unsigned long value; 349 unsigned int reg_data; 350 351 /* put system in a "safe" state : */ 352 /* disable audio interrupts */ 353 writel(0xffffffff, priv->io + KIRKWOOD_INT_CAUSE); 354 writel(0, priv->io + KIRKWOOD_INT_MASK); 355 356 reg_data = readl(priv->io + 0x1200); 357 reg_data &= (~(0x333FF8)); 358 reg_data |= 0x111D18; 359 writel(reg_data, priv->io + 0x1200); 360 361 msleep(500); 362 363 reg_data = readl(priv->io + 0x1200); 364 reg_data &= (~(0x333FF8)); 365 reg_data |= 0x111D18; 366 writel(reg_data, priv->io + 0x1200); 367 368 /* disable playback/record */ 369 value = readl(priv->io + KIRKWOOD_PLAYCTL); 370 value &= ~(KIRKWOOD_PLAYCTL_I2S_EN|KIRKWOOD_PLAYCTL_SPDIF_EN); 371 writel(value, priv->io + KIRKWOOD_PLAYCTL); 372 373 value = readl(priv->io + KIRKWOOD_RECCTL); 374 value &= ~(KIRKWOOD_RECCTL_I2S_EN | KIRKWOOD_RECCTL_SPDIF_EN); 375 writel(value, priv->io + KIRKWOOD_RECCTL); 376 377 return 0; 378 379 } 380 381 static int kirkwood_i2s_remove(struct snd_soc_dai *dai) 382 { 383 return 0; 384 } 385 386 static const struct snd_soc_dai_ops kirkwood_i2s_dai_ops = { 387 .startup = kirkwood_i2s_startup, 388 .trigger = kirkwood_i2s_trigger, 389 .hw_params = kirkwood_i2s_hw_params, 390 .set_fmt = kirkwood_i2s_set_fmt, 391 }; 392 393 394 static struct snd_soc_dai_driver kirkwood_i2s_dai = { 395 .probe = kirkwood_i2s_probe, 396 .remove = kirkwood_i2s_remove, 397 .playback = { 398 .channels_min = 1, 399 .channels_max = 2, 400 .rates = KIRKWOOD_I2S_RATES, 401 .formats = KIRKWOOD_I2S_FORMATS,}, 402 .capture = { 403 .channels_min = 1, 404 .channels_max = 2, 405 .rates = KIRKWOOD_I2S_RATES, 406 .formats = KIRKWOOD_I2S_FORMATS,}, 407 .ops = &kirkwood_i2s_dai_ops, 408 }; 409 410 static __devinit int kirkwood_i2s_dev_probe(struct platform_device *pdev) 411 { 412 struct resource *mem; 413 struct kirkwood_asoc_platform_data *data = 414 pdev->dev.platform_data; 415 struct kirkwood_dma_data *priv; 416 int err; 417 418 priv = kzalloc(sizeof(struct kirkwood_dma_data), GFP_KERNEL); 419 if (!priv) { 420 dev_err(&pdev->dev, "allocation failed\n"); 421 err = -ENOMEM; 422 goto error; 423 } 424 dev_set_drvdata(&pdev->dev, priv); 425 426 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 427 if (!mem) { 428 dev_err(&pdev->dev, "platform_get_resource failed\n"); 429 err = -ENXIO; 430 goto err_alloc; 431 } 432 433 priv->mem = request_mem_region(mem->start, SZ_16K, DRV_NAME); 434 if (!priv->mem) { 435 dev_err(&pdev->dev, "request_mem_region failed\n"); 436 err = -EBUSY; 437 goto err_alloc; 438 } 439 440 priv->io = ioremap(priv->mem->start, SZ_16K); 441 if (!priv->io) { 442 dev_err(&pdev->dev, "ioremap failed\n"); 443 err = -ENOMEM; 444 goto err_iomem; 445 } 446 447 priv->irq = platform_get_irq(pdev, 0); 448 if (priv->irq <= 0) { 449 dev_err(&pdev->dev, "platform_get_irq failed\n"); 450 err = -ENXIO; 451 goto err_ioremap; 452 } 453 454 if (!data) { 455 dev_err(&pdev->dev, "no platform data ?!\n"); 456 err = -EINVAL; 457 goto err_ioremap; 458 } 459 460 priv->burst = data->burst; 461 462 priv->clk = clk_get(&pdev->dev, NULL); 463 if (IS_ERR(priv->clk)) { 464 dev_err(&pdev->dev, "no clock\n"); 465 err = PTR_ERR(priv->clk); 466 goto err_ioremap; 467 } 468 clk_prepare_enable(priv->clk); 469 470 err = snd_soc_register_dai(&pdev->dev, &kirkwood_i2s_dai); 471 if (!err) 472 return 0; 473 dev_err(&pdev->dev, "snd_soc_register_dai failed\n"); 474 475 clk_disable_unprepare(priv->clk); 476 clk_put(priv->clk); 477 478 err_ioremap: 479 iounmap(priv->io); 480 err_iomem: 481 release_mem_region(priv->mem->start, SZ_16K); 482 err_alloc: 483 kfree(priv); 484 error: 485 return err; 486 } 487 488 static __devexit int kirkwood_i2s_dev_remove(struct platform_device *pdev) 489 { 490 struct kirkwood_dma_data *priv = dev_get_drvdata(&pdev->dev); 491 492 snd_soc_unregister_dai(&pdev->dev); 493 494 clk_disable_unprepare(priv->clk); 495 clk_put(priv->clk); 496 497 iounmap(priv->io); 498 release_mem_region(priv->mem->start, SZ_16K); 499 kfree(priv); 500 501 return 0; 502 } 503 504 static struct platform_driver kirkwood_i2s_driver = { 505 .probe = kirkwood_i2s_dev_probe, 506 .remove = __devexit_p(kirkwood_i2s_dev_remove), 507 .driver = { 508 .name = DRV_NAME, 509 .owner = THIS_MODULE, 510 }, 511 }; 512 513 module_platform_driver(kirkwood_i2s_driver); 514 515 /* Module information */ 516 MODULE_AUTHOR("Arnaud Patard, <arnaud.patard@rtp-net.org>"); 517 MODULE_DESCRIPTION("Kirkwood I2S SoC Interface"); 518 MODULE_LICENSE("GPL"); 519 MODULE_ALIAS("platform:kirkwood-i2s"); 520