Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.18 661 lines 16 kB view raw
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_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSI | 99 SSI_STCR_TEFS; 100 scr |= SSI_SCR_NET; 101 if (ssi->flags & IMX_SSI_USE_I2S_SLAVE) { 102 scr &= ~SSI_I2S_MODE_MASK; 103 scr |= SSI_SCR_I2S_MODE_SLAVE; 104 } 105 break; 106 case SND_SOC_DAIFMT_LEFT_J: 107 /* data on rising edge of bclk, frame high with data */ 108 strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP; 109 break; 110 case SND_SOC_DAIFMT_DSP_B: 111 /* data on rising edge of bclk, frame high with data */ 112 strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSL; 113 break; 114 case SND_SOC_DAIFMT_DSP_A: 115 /* data on rising edge of bclk, frame high 1clk before data */ 116 strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSL | 117 SSI_STCR_TEFS; 118 break; 119 } 120 121 /* DAI clock inversion */ 122 switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 123 case SND_SOC_DAIFMT_IB_IF: 124 strcr ^= SSI_STCR_TSCKP | SSI_STCR_TFSI; 125 break; 126 case SND_SOC_DAIFMT_IB_NF: 127 strcr ^= SSI_STCR_TSCKP; 128 break; 129 case SND_SOC_DAIFMT_NB_IF: 130 strcr ^= SSI_STCR_TFSI; 131 break; 132 case SND_SOC_DAIFMT_NB_NF: 133 break; 134 } 135 136 /* DAI clock master masks */ 137 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 138 case SND_SOC_DAIFMT_CBM_CFM: 139 break; 140 default: 141 /* Master mode not implemented, needs handling of clocks. */ 142 return -EINVAL; 143 } 144 145 strcr |= SSI_STCR_TFEN0; 146 147 if (ssi->flags & IMX_SSI_NET) 148 scr |= SSI_SCR_NET; 149 if (ssi->flags & IMX_SSI_SYN) 150 scr |= SSI_SCR_SYN; 151 152 writel(strcr, ssi->base + SSI_STCR); 153 writel(strcr, ssi->base + SSI_SRCR); 154 writel(scr, ssi->base + SSI_SCR); 155 156 return 0; 157} 158 159/* 160 * SSI system clock configuration. 161 * Should only be called when port is inactive (i.e. SSIEN = 0). 162 */ 163static int imx_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai, 164 int clk_id, unsigned int freq, int dir) 165{ 166 struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai); 167 u32 scr; 168 169 scr = readl(ssi->base + SSI_SCR); 170 171 switch (clk_id) { 172 case IMX_SSP_SYS_CLK: 173 if (dir == SND_SOC_CLOCK_OUT) 174 scr |= SSI_SCR_SYS_CLK_EN; 175 else 176 scr &= ~SSI_SCR_SYS_CLK_EN; 177 break; 178 default: 179 return -EINVAL; 180 } 181 182 writel(scr, ssi->base + SSI_SCR); 183 184 return 0; 185} 186 187/* 188 * SSI Clock dividers 189 * Should only be called when port is inactive (i.e. SSIEN = 0). 190 */ 191static int imx_ssi_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, 192 int div_id, int div) 193{ 194 struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai); 195 u32 stccr, srccr; 196 197 stccr = readl(ssi->base + SSI_STCCR); 198 srccr = readl(ssi->base + SSI_SRCCR); 199 200 switch (div_id) { 201 case IMX_SSI_TX_DIV_2: 202 stccr &= ~SSI_STCCR_DIV2; 203 stccr |= div; 204 break; 205 case IMX_SSI_TX_DIV_PSR: 206 stccr &= ~SSI_STCCR_PSR; 207 stccr |= div; 208 break; 209 case IMX_SSI_TX_DIV_PM: 210 stccr &= ~0xff; 211 stccr |= SSI_STCCR_PM(div); 212 break; 213 case IMX_SSI_RX_DIV_2: 214 stccr &= ~SSI_STCCR_DIV2; 215 stccr |= div; 216 break; 217 case IMX_SSI_RX_DIV_PSR: 218 stccr &= ~SSI_STCCR_PSR; 219 stccr |= div; 220 break; 221 case IMX_SSI_RX_DIV_PM: 222 stccr &= ~0xff; 223 stccr |= SSI_STCCR_PM(div); 224 break; 225 default: 226 return -EINVAL; 227 } 228 229 writel(stccr, ssi->base + SSI_STCCR); 230 writel(srccr, ssi->base + SSI_SRCCR); 231 232 return 0; 233} 234 235/* 236 * Should only be called when port is inactive (i.e. SSIEN = 0), 237 * although can be called multiple times by upper layers. 238 */ 239static int imx_ssi_hw_params(struct snd_pcm_substream *substream, 240 struct snd_pcm_hw_params *params, 241 struct snd_soc_dai *cpu_dai) 242{ 243 struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai); 244 u32 reg, sccr; 245 246 /* Tx/Rx config */ 247 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 248 reg = SSI_STCCR; 249 else 250 reg = SSI_SRCCR; 251 252 if (ssi->flags & IMX_SSI_SYN) 253 reg = SSI_STCCR; 254 255 sccr = readl(ssi->base + reg) & ~SSI_STCCR_WL_MASK; 256 257 /* DAI data (word) size */ 258 switch (params_format(params)) { 259 case SNDRV_PCM_FORMAT_S16_LE: 260 sccr |= SSI_SRCCR_WL(16); 261 break; 262 case SNDRV_PCM_FORMAT_S20_3LE: 263 sccr |= SSI_SRCCR_WL(20); 264 break; 265 case SNDRV_PCM_FORMAT_S24_LE: 266 sccr |= SSI_SRCCR_WL(24); 267 break; 268 } 269 270 writel(sccr, ssi->base + reg); 271 272 return 0; 273} 274 275static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd, 276 struct snd_soc_dai *dai) 277{ 278 struct imx_ssi *ssi = snd_soc_dai_get_drvdata(dai); 279 unsigned int sier_bits, sier; 280 unsigned int scr; 281 282 scr = readl(ssi->base + SSI_SCR); 283 sier = readl(ssi->base + SSI_SIER); 284 285 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 286 if (ssi->flags & IMX_SSI_DMA) 287 sier_bits = SSI_SIER_TDMAE; 288 else 289 sier_bits = SSI_SIER_TIE | SSI_SIER_TFE0_EN; 290 } else { 291 if (ssi->flags & IMX_SSI_DMA) 292 sier_bits = SSI_SIER_RDMAE; 293 else 294 sier_bits = SSI_SIER_RIE | SSI_SIER_RFF0_EN; 295 } 296 297 switch (cmd) { 298 case SNDRV_PCM_TRIGGER_START: 299 case SNDRV_PCM_TRIGGER_RESUME: 300 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 301 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 302 scr |= SSI_SCR_TE; 303 else 304 scr |= SSI_SCR_RE; 305 sier |= sier_bits; 306 307 scr |= SSI_SCR_SSIEN; 308 309 break; 310 311 case SNDRV_PCM_TRIGGER_STOP: 312 case SNDRV_PCM_TRIGGER_SUSPEND: 313 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 314 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 315 scr &= ~SSI_SCR_TE; 316 else 317 scr &= ~SSI_SCR_RE; 318 sier &= ~sier_bits; 319 320 if (!(scr & (SSI_SCR_TE | SSI_SCR_RE))) 321 scr &= ~SSI_SCR_SSIEN; 322 323 break; 324 default: 325 return -EINVAL; 326 } 327 328 if (!(ssi->flags & IMX_SSI_USE_AC97)) 329 /* rx/tx are always enabled to access ac97 registers */ 330 writel(scr, ssi->base + SSI_SCR); 331 332 writel(sier, ssi->base + SSI_SIER); 333 334 return 0; 335} 336 337static const struct snd_soc_dai_ops imx_ssi_pcm_dai_ops = { 338 .hw_params = imx_ssi_hw_params, 339 .set_fmt = imx_ssi_set_dai_fmt, 340 .set_clkdiv = imx_ssi_set_dai_clkdiv, 341 .set_sysclk = imx_ssi_set_dai_sysclk, 342 .set_tdm_slot = imx_ssi_set_dai_tdm_slot, 343 .trigger = imx_ssi_trigger, 344}; 345 346static int imx_ssi_dai_probe(struct snd_soc_dai *dai) 347{ 348 struct imx_ssi *ssi = dev_get_drvdata(dai->dev); 349 uint32_t val; 350 351 snd_soc_dai_set_drvdata(dai, ssi); 352 353 val = SSI_SFCSR_TFWM0(ssi->dma_params_tx.maxburst) | 354 SSI_SFCSR_RFWM0(ssi->dma_params_rx.maxburst); 355 writel(val, ssi->base + SSI_SFCSR); 356 357 /* Tx/Rx config */ 358 dai->playback_dma_data = &ssi->dma_params_tx; 359 dai->capture_dma_data = &ssi->dma_params_rx; 360 361 return 0; 362} 363 364static struct snd_soc_dai_driver imx_ssi_dai = { 365 .probe = imx_ssi_dai_probe, 366 .playback = { 367 .channels_min = 1, 368 .channels_max = 2, 369 .rates = SNDRV_PCM_RATE_8000_96000, 370 .formats = SNDRV_PCM_FMTBIT_S16_LE, 371 }, 372 .capture = { 373 .channels_min = 1, 374 .channels_max = 2, 375 .rates = SNDRV_PCM_RATE_8000_96000, 376 .formats = SNDRV_PCM_FMTBIT_S16_LE, 377 }, 378 .ops = &imx_ssi_pcm_dai_ops, 379}; 380 381static struct snd_soc_dai_driver imx_ac97_dai = { 382 .probe = imx_ssi_dai_probe, 383 .bus_control = true, 384 .playback = { 385 .stream_name = "AC97 Playback", 386 .channels_min = 2, 387 .channels_max = 2, 388 .rates = SNDRV_PCM_RATE_8000_48000, 389 .formats = SNDRV_PCM_FMTBIT_S16_LE, 390 }, 391 .capture = { 392 .stream_name = "AC97 Capture", 393 .channels_min = 2, 394 .channels_max = 2, 395 .rates = SNDRV_PCM_RATE_48000, 396 .formats = SNDRV_PCM_FMTBIT_S16_LE, 397 }, 398 .ops = &imx_ssi_pcm_dai_ops, 399}; 400 401static const struct snd_soc_component_driver imx_component = { 402 .name = DRV_NAME, 403}; 404 405static void setup_channel_to_ac97(struct imx_ssi *imx_ssi) 406{ 407 void __iomem *base = imx_ssi->base; 408 409 writel(0x0, base + SSI_SCR); 410 writel(0x0, base + SSI_STCR); 411 writel(0x0, base + SSI_SRCR); 412 413 writel(SSI_SCR_SYN | SSI_SCR_NET, base + SSI_SCR); 414 415 writel(SSI_SFCSR_RFWM0(8) | 416 SSI_SFCSR_TFWM0(8) | 417 SSI_SFCSR_RFWM1(8) | 418 SSI_SFCSR_TFWM1(8), base + SSI_SFCSR); 419 420 writel(SSI_STCCR_WL(16) | SSI_STCCR_DC(12), base + SSI_STCCR); 421 writel(SSI_STCCR_WL(16) | SSI_STCCR_DC(12), base + SSI_SRCCR); 422 423 writel(SSI_SCR_SYN | SSI_SCR_NET | SSI_SCR_SSIEN, base + SSI_SCR); 424 writel(SSI_SOR_WAIT(3), base + SSI_SOR); 425 426 writel(SSI_SCR_SYN | SSI_SCR_NET | SSI_SCR_SSIEN | 427 SSI_SCR_TE | SSI_SCR_RE, 428 base + SSI_SCR); 429 430 writel(SSI_SACNT_DEFAULT, base + SSI_SACNT); 431 writel(0xff, base + SSI_SACCDIS); 432 writel(0x300, base + SSI_SACCEN); 433} 434 435static struct imx_ssi *ac97_ssi; 436 437static void imx_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, 438 unsigned short val) 439{ 440 struct imx_ssi *imx_ssi = ac97_ssi; 441 void __iomem *base = imx_ssi->base; 442 unsigned int lreg; 443 unsigned int lval; 444 445 if (reg > 0x7f) 446 return; 447 448 pr_debug("%s: 0x%02x 0x%04x\n", __func__, reg, val); 449 450 lreg = reg << 12; 451 writel(lreg, base + SSI_SACADD); 452 453 lval = val << 4; 454 writel(lval , base + SSI_SACDAT); 455 456 writel(SSI_SACNT_DEFAULT | SSI_SACNT_WR, base + SSI_SACNT); 457 udelay(100); 458} 459 460static unsigned short imx_ssi_ac97_read(struct snd_ac97 *ac97, 461 unsigned short reg) 462{ 463 struct imx_ssi *imx_ssi = ac97_ssi; 464 void __iomem *base = imx_ssi->base; 465 466 unsigned short val = -1; 467 unsigned int lreg; 468 469 lreg = (reg & 0x7f) << 12 ; 470 writel(lreg, base + SSI_SACADD); 471 writel(SSI_SACNT_DEFAULT | SSI_SACNT_RD, base + SSI_SACNT); 472 473 udelay(100); 474 475 val = (readl(base + SSI_SACDAT) >> 4) & 0xffff; 476 477 pr_debug("%s: 0x%02x 0x%04x\n", __func__, reg, val); 478 479 return val; 480} 481 482static void imx_ssi_ac97_reset(struct snd_ac97 *ac97) 483{ 484 struct imx_ssi *imx_ssi = ac97_ssi; 485 486 if (imx_ssi->ac97_reset) 487 imx_ssi->ac97_reset(ac97); 488 /* First read sometimes fails, do a dummy read */ 489 imx_ssi_ac97_read(ac97, 0); 490} 491 492static void imx_ssi_ac97_warm_reset(struct snd_ac97 *ac97) 493{ 494 struct imx_ssi *imx_ssi = ac97_ssi; 495 496 if (imx_ssi->ac97_warm_reset) 497 imx_ssi->ac97_warm_reset(ac97); 498 499 /* First read sometimes fails, do a dummy read */ 500 imx_ssi_ac97_read(ac97, 0); 501} 502 503static struct snd_ac97_bus_ops imx_ssi_ac97_ops = { 504 .read = imx_ssi_ac97_read, 505 .write = imx_ssi_ac97_write, 506 .reset = imx_ssi_ac97_reset, 507 .warm_reset = imx_ssi_ac97_warm_reset 508}; 509 510static int imx_ssi_probe(struct platform_device *pdev) 511{ 512 struct resource *res; 513 struct imx_ssi *ssi; 514 struct imx_ssi_platform_data *pdata = pdev->dev.platform_data; 515 int ret = 0; 516 struct snd_soc_dai_driver *dai; 517 518 ssi = devm_kzalloc(&pdev->dev, sizeof(*ssi), GFP_KERNEL); 519 if (!ssi) 520 return -ENOMEM; 521 dev_set_drvdata(&pdev->dev, ssi); 522 523 if (pdata) { 524 ssi->ac97_reset = pdata->ac97_reset; 525 ssi->ac97_warm_reset = pdata->ac97_warm_reset; 526 ssi->flags = pdata->flags; 527 } 528 529 ssi->irq = platform_get_irq(pdev, 0); 530 if (ssi->irq < 0) { 531 dev_err(&pdev->dev, "Failed to get IRQ: %d\n", ssi->irq); 532 return ssi->irq; 533 } 534 535 ssi->clk = devm_clk_get(&pdev->dev, NULL); 536 if (IS_ERR(ssi->clk)) { 537 ret = PTR_ERR(ssi->clk); 538 dev_err(&pdev->dev, "Cannot get the clock: %d\n", 539 ret); 540 goto failed_clk; 541 } 542 ret = clk_prepare_enable(ssi->clk); 543 if (ret) 544 goto failed_clk; 545 546 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 547 ssi->base = devm_ioremap_resource(&pdev->dev, res); 548 if (IS_ERR(ssi->base)) { 549 ret = PTR_ERR(ssi->base); 550 goto failed_register; 551 } 552 553 if (ssi->flags & IMX_SSI_USE_AC97) { 554 if (ac97_ssi) { 555 dev_err(&pdev->dev, "AC'97 SSI already registered\n"); 556 ret = -EBUSY; 557 goto failed_register; 558 } 559 ac97_ssi = ssi; 560 setup_channel_to_ac97(ssi); 561 dai = &imx_ac97_dai; 562 } else 563 dai = &imx_ssi_dai; 564 565 writel(0x0, ssi->base + SSI_SIER); 566 567 ssi->dma_params_rx.addr = res->start + SSI_SRX0; 568 ssi->dma_params_tx.addr = res->start + SSI_STX0; 569 570 ssi->dma_params_tx.maxburst = 6; 571 ssi->dma_params_rx.maxburst = 4; 572 573 ssi->dma_params_tx.filter_data = &ssi->filter_data_tx; 574 ssi->dma_params_rx.filter_data = &ssi->filter_data_rx; 575 576 res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx0"); 577 if (res) { 578 imx_pcm_dma_params_init_data(&ssi->filter_data_tx, res->start, 579 IMX_DMATYPE_SSI); 580 } 581 582 res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx0"); 583 if (res) { 584 imx_pcm_dma_params_init_data(&ssi->filter_data_rx, res->start, 585 IMX_DMATYPE_SSI); 586 } 587 588 platform_set_drvdata(pdev, ssi); 589 590 ret = snd_soc_set_ac97_ops(&imx_ssi_ac97_ops); 591 if (ret != 0) { 592 dev_err(&pdev->dev, "Failed to set AC'97 ops: %d\n", ret); 593 goto failed_register; 594 } 595 596 ret = snd_soc_register_component(&pdev->dev, &imx_component, 597 dai, 1); 598 if (ret) { 599 dev_err(&pdev->dev, "register DAI failed\n"); 600 goto failed_register; 601 } 602 603 ssi->fiq_params.irq = ssi->irq; 604 ssi->fiq_params.base = ssi->base; 605 ssi->fiq_params.dma_params_rx = &ssi->dma_params_rx; 606 ssi->fiq_params.dma_params_tx = &ssi->dma_params_tx; 607 608 ssi->fiq_init = imx_pcm_fiq_init(pdev, &ssi->fiq_params); 609 ssi->dma_init = imx_pcm_dma_init(pdev, IMX_SSI_DMABUF_SIZE); 610 611 if (ssi->fiq_init && ssi->dma_init) { 612 ret = ssi->fiq_init; 613 goto failed_pcm; 614 } 615 616 return 0; 617 618failed_pcm: 619 snd_soc_unregister_component(&pdev->dev); 620failed_register: 621 clk_disable_unprepare(ssi->clk); 622failed_clk: 623 snd_soc_set_ac97_ops(NULL); 624 625 return ret; 626} 627 628static int imx_ssi_remove(struct platform_device *pdev) 629{ 630 struct imx_ssi *ssi = platform_get_drvdata(pdev); 631 632 if (!ssi->fiq_init) 633 imx_pcm_fiq_exit(pdev); 634 635 snd_soc_unregister_component(&pdev->dev); 636 637 if (ssi->flags & IMX_SSI_USE_AC97) 638 ac97_ssi = NULL; 639 640 clk_disable_unprepare(ssi->clk); 641 snd_soc_set_ac97_ops(NULL); 642 643 return 0; 644} 645 646static struct platform_driver imx_ssi_driver = { 647 .probe = imx_ssi_probe, 648 .remove = imx_ssi_remove, 649 650 .driver = { 651 .name = "imx-ssi", 652 }, 653}; 654 655module_platform_driver(imx_ssi_driver); 656 657/* Module information */ 658MODULE_AUTHOR("Sascha Hauer, <s.hauer@pengutronix.de>"); 659MODULE_DESCRIPTION("i.MX I2S/ac97 SoC Interface"); 660MODULE_LICENSE("GPL"); 661MODULE_ALIAS("platform:imx-ssi");