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