1// SPDX-License-Identifier: GPL-2.0+ 2// 3// imx-ssi.c -- ALSA Soc Audio Layer 4// 5// Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de> 6// 7// This code is based on code copyrighted by Freescale, 8// Liam Girdwood, Javier Martin and probably others. 9// 10// The i.MX SSI core has some nasty limitations in AC97 mode. While most 11// sane processor vendors have a FIFO per AC97 slot, the i.MX has only 12// one FIFO which combines all valid receive slots. We cannot even select 13// which slots we want to receive. The WM9712 with which this driver 14// was developed with always sends GPIO status data in slot 12 which 15// we receive in our (PCM-) data stream. The only chance we have is to 16// manually skip this data in the FIQ handler. With sampling rates different 17// from 48000Hz not every frame has valid receive data, so the ratio 18// between pcm data and GPIO status data changes. Our FIQ handler is not 19// able to handle this, hence this driver only works with 48000Hz sampling 20// rate. 21// Reading and writing AC97 registers is another challenge. The core 22// provides us status bits when the read register is updated with *another* 23// value. When we read the same register two times (and the register still 24// contains the same value) these status bits are not set. We work 25// around this by not polling these bits but only wait a fixed delay. 26 27#include <linux/clk.h> 28#include <linux/delay.h> 29#include <linux/device.h> 30#include <linux/dma-mapping.h> 31#include <linux/init.h> 32#include <linux/interrupt.h> 33#include <linux/module.h> 34#include <linux/platform_device.h> 35#include <linux/slab.h> 36 37#include <sound/core.h> 38#include <sound/initval.h> 39#include <sound/pcm.h> 40#include <sound/pcm_params.h> 41#include <sound/soc.h> 42 43#include <linux/platform_data/asoc-imx-ssi.h> 44 45#include "imx-ssi.h" 46#include "fsl_utils.h" 47 48#define SSI_SACNT_DEFAULT (SSI_SACNT_AC97EN | SSI_SACNT_FV) 49 50/* 51 * SSI Network Mode or TDM slots configuration. 52 * Should only be called when port is inactive (i.e. SSIEN = 0). 53 */ 54static int imx_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, 55 unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) 56{ 57 struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai); 58 u32 sccr; 59 60 sccr = readl(ssi->base + SSI_STCCR); 61 sccr &= ~SSI_STCCR_DC_MASK; 62 sccr |= SSI_STCCR_DC(slots - 1); 63 writel(sccr, ssi->base + SSI_STCCR); 64 65 sccr = readl(ssi->base + SSI_SRCCR); 66 sccr &= ~SSI_STCCR_DC_MASK; 67 sccr |= SSI_STCCR_DC(slots - 1); 68 writel(sccr, ssi->base + SSI_SRCCR); 69 70 writel(~tx_mask, ssi->base + SSI_STMSK); 71 writel(~rx_mask, ssi->base + SSI_SRMSK); 72 73 return 0; 74} 75 76/* 77 * SSI DAI format configuration. 78 * Should only be called when port is inactive (i.e. SSIEN = 0). 79 */ 80static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) 81{ 82 struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai); 83 u32 strcr = 0, scr; 84 85 scr = readl(ssi->base + SSI_SCR) & ~(SSI_SCR_SYN | SSI_SCR_NET); 86 87 /* DAI mode */ 88 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 89 case SND_SOC_DAIFMT_I2S: 90 /* data on rising edge of bclk, frame low 1clk before data */ 91 strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSI | 92 SSI_STCR_TEFS; 93 scr |= SSI_SCR_NET; 94 if (ssi->flags & IMX_SSI_USE_I2S_SLAVE) { 95 scr &= ~SSI_I2S_MODE_MASK; 96 scr |= SSI_SCR_I2S_MODE_SLAVE; 97 } 98 break; 99 case SND_SOC_DAIFMT_LEFT_J: 100 /* data on rising edge of bclk, frame high with data */ 101 strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP; 102 break; 103 case SND_SOC_DAIFMT_DSP_B: 104 /* data on rising edge of bclk, frame high with data */ 105 strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSL; 106 break; 107 case SND_SOC_DAIFMT_DSP_A: 108 /* data on rising edge of bclk, frame high 1clk before data */ 109 strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSL | 110 SSI_STCR_TEFS; 111 break; 112 } 113 114 /* DAI clock inversion */ 115 switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 116 case SND_SOC_DAIFMT_IB_IF: 117 strcr ^= SSI_STCR_TSCKP | SSI_STCR_TFSI; 118 break; 119 case SND_SOC_DAIFMT_IB_NF: 120 strcr ^= SSI_STCR_TSCKP; 121 break; 122 case SND_SOC_DAIFMT_NB_IF: 123 strcr ^= SSI_STCR_TFSI; 124 break; 125 case SND_SOC_DAIFMT_NB_NF: 126 break; 127 } 128 129 /* DAI clock master masks */ 130 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 131 case SND_SOC_DAIFMT_CBM_CFM: 132 break; 133 default: 134 /* Master mode not implemented, needs handling of clocks. */ 135 return -EINVAL; 136 } 137 138 strcr |= SSI_STCR_TFEN0; 139 140 if (ssi->flags & IMX_SSI_NET) 141 scr |= SSI_SCR_NET; 142 if (ssi->flags & IMX_SSI_SYN) 143 scr |= SSI_SCR_SYN; 144 145 writel(strcr, ssi->base + SSI_STCR); 146 writel(strcr, ssi->base + SSI_SRCR); 147 writel(scr, ssi->base + SSI_SCR); 148 149 return 0; 150} 151 152/* 153 * SSI system clock configuration. 154 * Should only be called when port is inactive (i.e. SSIEN = 0). 155 */ 156static int imx_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai, 157 int clk_id, unsigned int freq, int dir) 158{ 159 struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai); 160 u32 scr; 161 162 scr = readl(ssi->base + SSI_SCR); 163 164 switch (clk_id) { 165 case IMX_SSP_SYS_CLK: 166 if (dir == SND_SOC_CLOCK_OUT) 167 scr |= SSI_SCR_SYS_CLK_EN; 168 else 169 scr &= ~SSI_SCR_SYS_CLK_EN; 170 break; 171 default: 172 return -EINVAL; 173 } 174 175 writel(scr, ssi->base + SSI_SCR); 176 177 return 0; 178} 179 180/* 181 * SSI Clock dividers 182 * Should only be called when port is inactive (i.e. SSIEN = 0). 183 */ 184static int imx_ssi_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, 185 int div_id, int div) 186{ 187 struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai); 188 u32 stccr, srccr; 189 190 stccr = readl(ssi->base + SSI_STCCR); 191 srccr = readl(ssi->base + SSI_SRCCR); 192 193 switch (div_id) { 194 case IMX_SSI_TX_DIV_2: 195 stccr &= ~SSI_STCCR_DIV2; 196 stccr |= div; 197 break; 198 case IMX_SSI_TX_DIV_PSR: 199 stccr &= ~SSI_STCCR_PSR; 200 stccr |= div; 201 break; 202 case IMX_SSI_TX_DIV_PM: 203 stccr &= ~0xff; 204 stccr |= SSI_STCCR_PM(div); 205 break; 206 case IMX_SSI_RX_DIV_2: 207 stccr &= ~SSI_STCCR_DIV2; 208 stccr |= div; 209 break; 210 case IMX_SSI_RX_DIV_PSR: 211 stccr &= ~SSI_STCCR_PSR; 212 stccr |= div; 213 break; 214 case IMX_SSI_RX_DIV_PM: 215 stccr &= ~0xff; 216 stccr |= SSI_STCCR_PM(div); 217 break; 218 default: 219 return -EINVAL; 220 } 221 222 writel(stccr, ssi->base + SSI_STCCR); 223 writel(srccr, ssi->base + SSI_SRCCR); 224 225 return 0; 226} 227 228/* 229 * Should only be called when port is inactive (i.e. SSIEN = 0), 230 * although can be called multiple times by upper layers. 231 */ 232static int imx_ssi_hw_params(struct snd_pcm_substream *substream, 233 struct snd_pcm_hw_params *params, 234 struct snd_soc_dai *cpu_dai) 235{ 236 struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai); 237 u32 reg, sccr; 238 239 /* Tx/Rx config */ 240 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 241 reg = SSI_STCCR; 242 else 243 reg = SSI_SRCCR; 244 245 if (ssi->flags & IMX_SSI_SYN) 246 reg = SSI_STCCR; 247 248 sccr = readl(ssi->base + reg) & ~SSI_STCCR_WL_MASK; 249 250 /* DAI data (word) size */ 251 switch (params_format(params)) { 252 case SNDRV_PCM_FORMAT_S16_LE: 253 sccr |= SSI_SRCCR_WL(16); 254 break; 255 case SNDRV_PCM_FORMAT_S20_3LE: 256 sccr |= SSI_SRCCR_WL(20); 257 break; 258 case SNDRV_PCM_FORMAT_S24_LE: 259 sccr |= SSI_SRCCR_WL(24); 260 break; 261 } 262 263 writel(sccr, ssi->base + reg); 264 265 return 0; 266} 267 268static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd, 269 struct snd_soc_dai *dai) 270{ 271 struct imx_ssi *ssi = snd_soc_dai_get_drvdata(dai); 272 unsigned int sier_bits, sier; 273 unsigned int scr; 274 275 scr = readl(ssi->base + SSI_SCR); 276 sier = readl(ssi->base + SSI_SIER); 277 278 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 279 if (ssi->flags & IMX_SSI_DMA) 280 sier_bits = SSI_SIER_TDMAE; 281 else 282 sier_bits = SSI_SIER_TIE | SSI_SIER_TFE0_EN; 283 } else { 284 if (ssi->flags & IMX_SSI_DMA) 285 sier_bits = SSI_SIER_RDMAE; 286 else 287 sier_bits = SSI_SIER_RIE | SSI_SIER_RFF0_EN; 288 } 289 290 switch (cmd) { 291 case SNDRV_PCM_TRIGGER_START: 292 case SNDRV_PCM_TRIGGER_RESUME: 293 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 294 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 295 scr |= SSI_SCR_TE; 296 else 297 scr |= SSI_SCR_RE; 298 sier |= sier_bits; 299 300 scr |= SSI_SCR_SSIEN; 301 302 break; 303 304 case SNDRV_PCM_TRIGGER_STOP: 305 case SNDRV_PCM_TRIGGER_SUSPEND: 306 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 307 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 308 scr &= ~SSI_SCR_TE; 309 else 310 scr &= ~SSI_SCR_RE; 311 sier &= ~sier_bits; 312 313 if (!(scr & (SSI_SCR_TE | SSI_SCR_RE))) 314 scr &= ~SSI_SCR_SSIEN; 315 316 break; 317 default: 318 return -EINVAL; 319 } 320 321 if (!(ssi->flags & IMX_SSI_USE_AC97)) 322 /* rx/tx are always enabled to access ac97 registers */ 323 writel(scr, ssi->base + SSI_SCR); 324 325 writel(sier, ssi->base + SSI_SIER); 326 327 return 0; 328} 329 330static const struct snd_soc_dai_ops imx_ssi_pcm_dai_ops = { 331 .hw_params = imx_ssi_hw_params, 332 .set_fmt = imx_ssi_set_dai_fmt, 333 .set_clkdiv = imx_ssi_set_dai_clkdiv, 334 .set_sysclk = imx_ssi_set_dai_sysclk, 335 .set_tdm_slot = imx_ssi_set_dai_tdm_slot, 336 .trigger = imx_ssi_trigger, 337}; 338 339static int imx_ssi_dai_probe(struct snd_soc_dai *dai) 340{ 341 struct imx_ssi *ssi = dev_get_drvdata(dai->dev); 342 uint32_t val; 343 344 snd_soc_dai_set_drvdata(dai, ssi); 345 346 val = SSI_SFCSR_TFWM0(ssi->dma_params_tx.maxburst) | 347 SSI_SFCSR_RFWM0(ssi->dma_params_rx.maxburst); 348 writel(val, ssi->base + SSI_SFCSR); 349 350 /* Tx/Rx config */ 351 dai->playback_dma_data = &ssi->dma_params_tx; 352 dai->capture_dma_data = &ssi->dma_params_rx; 353 354 return 0; 355} 356 357static struct snd_soc_dai_driver imx_ssi_dai = { 358 .probe = imx_ssi_dai_probe, 359 .playback = { 360 .channels_min = 1, 361 .channels_max = 2, 362 .rates = SNDRV_PCM_RATE_8000_96000, 363 .formats = SNDRV_PCM_FMTBIT_S16_LE, 364 }, 365 .capture = { 366 .channels_min = 1, 367 .channels_max = 2, 368 .rates = SNDRV_PCM_RATE_8000_96000, 369 .formats = SNDRV_PCM_FMTBIT_S16_LE, 370 }, 371 .ops = &imx_ssi_pcm_dai_ops, 372}; 373 374static struct snd_soc_dai_driver imx_ac97_dai = { 375 .probe = imx_ssi_dai_probe, 376 .playback = { 377 .stream_name = "AC97 Playback", 378 .channels_min = 2, 379 .channels_max = 2, 380 .rates = SNDRV_PCM_RATE_8000_48000, 381 .formats = SNDRV_PCM_FMTBIT_S16_LE, 382 }, 383 .capture = { 384 .stream_name = "AC97 Capture", 385 .channels_min = 2, 386 .channels_max = 2, 387 .rates = SNDRV_PCM_RATE_48000, 388 .formats = SNDRV_PCM_FMTBIT_S16_LE, 389 }, 390 .ops = &imx_ssi_pcm_dai_ops, 391}; 392 393static const struct snd_soc_component_driver imx_component = { 394 .name = DRV_NAME, 395}; 396 397static void setup_channel_to_ac97(struct imx_ssi *imx_ssi) 398{ 399 void __iomem *base = imx_ssi->base; 400 401 writel(0x0, base + SSI_SCR); 402 writel(0x0, base + SSI_STCR); 403 writel(0x0, base + SSI_SRCR); 404 405 writel(SSI_SCR_SYN | SSI_SCR_NET, base + SSI_SCR); 406 407 writel(SSI_SFCSR_RFWM0(8) | 408 SSI_SFCSR_TFWM0(8) | 409 SSI_SFCSR_RFWM1(8) | 410 SSI_SFCSR_TFWM1(8), base + SSI_SFCSR); 411 412 writel(SSI_STCCR_WL(16) | SSI_STCCR_DC(12), base + SSI_STCCR); 413 writel(SSI_STCCR_WL(16) | SSI_STCCR_DC(12), base + SSI_SRCCR); 414 415 writel(SSI_SCR_SYN | SSI_SCR_NET | SSI_SCR_SSIEN, base + SSI_SCR); 416 writel(SSI_SOR_WAIT(3), base + SSI_SOR); 417 418 writel(SSI_SCR_SYN | SSI_SCR_NET | SSI_SCR_SSIEN | 419 SSI_SCR_TE | SSI_SCR_RE, 420 base + SSI_SCR); 421 422 writel(SSI_SACNT_DEFAULT, base + SSI_SACNT); 423 writel(0xff, base + SSI_SACCDIS); 424 writel(0x300, base + SSI_SACCEN); 425} 426 427static struct imx_ssi *ac97_ssi; 428 429static void imx_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, 430 unsigned short val) 431{ 432 struct imx_ssi *imx_ssi = ac97_ssi; 433 void __iomem *base = imx_ssi->base; 434 unsigned int lreg; 435 unsigned int lval; 436 437 if (reg > 0x7f) 438 return; 439 440 pr_debug("%s: 0x%02x 0x%04x\n", __func__, reg, val); 441 442 lreg = reg << 12; 443 writel(lreg, base + SSI_SACADD); 444 445 lval = val << 4; 446 writel(lval , base + SSI_SACDAT); 447 448 writel(SSI_SACNT_DEFAULT | SSI_SACNT_WR, base + SSI_SACNT); 449 udelay(100); 450} 451 452static unsigned short imx_ssi_ac97_read(struct snd_ac97 *ac97, 453 unsigned short reg) 454{ 455 struct imx_ssi *imx_ssi = ac97_ssi; 456 void __iomem *base = imx_ssi->base; 457 458 unsigned short val = -1; 459 unsigned int lreg; 460 461 lreg = (reg & 0x7f) << 12 ; 462 writel(lreg, base + SSI_SACADD); 463 writel(SSI_SACNT_DEFAULT | SSI_SACNT_RD, base + SSI_SACNT); 464 465 udelay(100); 466 467 val = (readl(base + SSI_SACDAT) >> 4) & 0xffff; 468 469 pr_debug("%s: 0x%02x 0x%04x\n", __func__, reg, val); 470 471 return val; 472} 473 474static void imx_ssi_ac97_reset(struct snd_ac97 *ac97) 475{ 476 struct imx_ssi *imx_ssi = ac97_ssi; 477 478 if (imx_ssi->ac97_reset) 479 imx_ssi->ac97_reset(ac97); 480 /* First read sometimes fails, do a dummy read */ 481 imx_ssi_ac97_read(ac97, 0); 482} 483 484static void imx_ssi_ac97_warm_reset(struct snd_ac97 *ac97) 485{ 486 struct imx_ssi *imx_ssi = ac97_ssi; 487 488 if (imx_ssi->ac97_warm_reset) 489 imx_ssi->ac97_warm_reset(ac97); 490 491 /* First read sometimes fails, do a dummy read */ 492 imx_ssi_ac97_read(ac97, 0); 493} 494 495static struct snd_ac97_bus_ops imx_ssi_ac97_ops = { 496 .read = imx_ssi_ac97_read, 497 .write = imx_ssi_ac97_write, 498 .reset = imx_ssi_ac97_reset, 499 .warm_reset = imx_ssi_ac97_warm_reset 500}; 501 502static int imx_ssi_probe(struct platform_device *pdev) 503{ 504 struct resource *res; 505 struct imx_ssi *ssi; 506 struct imx_ssi_platform_data *pdata = pdev->dev.platform_data; 507 int ret = 0; 508 struct snd_soc_dai_driver *dai; 509 510 ssi = devm_kzalloc(&pdev->dev, sizeof(*ssi), GFP_KERNEL); 511 if (!ssi) 512 return -ENOMEM; 513 dev_set_drvdata(&pdev->dev, ssi); 514 515 if (pdata) { 516 ssi->ac97_reset = pdata->ac97_reset; 517 ssi->ac97_warm_reset = pdata->ac97_warm_reset; 518 ssi->flags = pdata->flags; 519 } 520 521 ssi->irq = platform_get_irq(pdev, 0); 522 if (ssi->irq < 0) 523 return ssi->irq; 524 525 ssi->clk = devm_clk_get(&pdev->dev, NULL); 526 if (IS_ERR(ssi->clk)) { 527 ret = PTR_ERR(ssi->clk); 528 dev_err(&pdev->dev, "Cannot get the clock: %d\n", 529 ret); 530 goto failed_clk; 531 } 532 ret = clk_prepare_enable(ssi->clk); 533 if (ret) 534 goto failed_clk; 535 536 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 537 ssi->base = devm_ioremap_resource(&pdev->dev, res); 538 if (IS_ERR(ssi->base)) { 539 ret = PTR_ERR(ssi->base); 540 goto failed_register; 541 } 542 543 if (ssi->flags & IMX_SSI_USE_AC97) { 544 if (ac97_ssi) { 545 dev_err(&pdev->dev, "AC'97 SSI already registered\n"); 546 ret = -EBUSY; 547 goto failed_register; 548 } 549 ac97_ssi = ssi; 550 setup_channel_to_ac97(ssi); 551 dai = &imx_ac97_dai; 552 } else 553 dai = &imx_ssi_dai; 554 555 writel(0x0, ssi->base + SSI_SIER); 556 557 ssi->dma_params_rx.addr = res->start + SSI_SRX0; 558 ssi->dma_params_tx.addr = res->start + SSI_STX0; 559 560 ssi->dma_params_tx.maxburst = 6; 561 ssi->dma_params_rx.maxburst = 4; 562 563 ssi->dma_params_tx.filter_data = &ssi->filter_data_tx; 564 ssi->dma_params_rx.filter_data = &ssi->filter_data_rx; 565 566 res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx0"); 567 if (res) { 568 imx_pcm_dma_params_init_data(&ssi->filter_data_tx, res->start, 569 IMX_DMATYPE_SSI); 570 } 571 572 res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx0"); 573 if (res) { 574 imx_pcm_dma_params_init_data(&ssi->filter_data_rx, res->start, 575 IMX_DMATYPE_SSI); 576 } 577 578 platform_set_drvdata(pdev, ssi); 579 580 ret = snd_soc_set_ac97_ops(&imx_ssi_ac97_ops); 581 if (ret != 0) { 582 dev_err(&pdev->dev, "Failed to set AC'97 ops: %d\n", ret); 583 goto failed_register; 584 } 585 586 ret = snd_soc_register_component(&pdev->dev, &imx_component, 587 dai, 1); 588 if (ret) { 589 dev_err(&pdev->dev, "register DAI failed\n"); 590 goto failed_register; 591 } 592 593 ssi->fiq_params.irq = ssi->irq; 594 ssi->fiq_params.base = ssi->base; 595 ssi->fiq_params.dma_params_rx = &ssi->dma_params_rx; 596 ssi->fiq_params.dma_params_tx = &ssi->dma_params_tx; 597 598 ssi->fiq_init = imx_pcm_fiq_init(pdev, &ssi->fiq_params); 599 ssi->dma_init = imx_pcm_dma_init(pdev, IMX_SSI_DMABUF_SIZE); 600 601 if (ssi->fiq_init && ssi->dma_init) { 602 ret = ssi->fiq_init; 603 goto failed_pcm; 604 } 605 606 return 0; 607 608failed_pcm: 609 snd_soc_unregister_component(&pdev->dev); 610failed_register: 611 clk_disable_unprepare(ssi->clk); 612failed_clk: 613 snd_soc_set_ac97_ops(NULL); 614 615 return ret; 616} 617 618static int imx_ssi_remove(struct platform_device *pdev) 619{ 620 struct imx_ssi *ssi = platform_get_drvdata(pdev); 621 622 if (!ssi->fiq_init) 623 imx_pcm_fiq_exit(pdev); 624 625 snd_soc_unregister_component(&pdev->dev); 626 627 if (ssi->flags & IMX_SSI_USE_AC97) 628 ac97_ssi = NULL; 629 630 clk_disable_unprepare(ssi->clk); 631 snd_soc_set_ac97_ops(NULL); 632 633 return 0; 634} 635 636static struct platform_driver imx_ssi_driver = { 637 .probe = imx_ssi_probe, 638 .remove = imx_ssi_remove, 639 640 .driver = { 641 .name = "imx-ssi", 642 }, 643}; 644 645module_platform_driver(imx_ssi_driver); 646 647/* Module information */ 648MODULE_AUTHOR("Sascha Hauer, <s.hauer@pengutronix.de>"); 649MODULE_DESCRIPTION("i.MX I2S/ac97 SoC Interface"); 650MODULE_LICENSE("GPL"); 651MODULE_ALIAS("platform:imx-ssi"); 652