1/* 2 * ALSA SoC I2S Audio Layer for the Stretch S6000 family 3 * 4 * Author: Daniel Gloeckner, <dg@emlix.com> 5 * Copyright: (C) 2009 emlix GmbH <info@emlix.com> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 */ 11 12#include <linux/init.h> 13#include <linux/module.h> 14#include <linux/device.h> 15#include <linux/delay.h> 16#include <linux/clk.h> 17#include <linux/interrupt.h> 18#include <linux/io.h> 19#include <linux/slab.h> 20 21#include <sound/core.h> 22#include <sound/pcm.h> 23#include <sound/pcm_params.h> 24#include <sound/initval.h> 25#include <sound/soc.h> 26 27#include "s6000-i2s.h" 28#include "s6000-pcm.h" 29 30struct s6000_i2s_dev { 31 dma_addr_t sifbase; 32 u8 __iomem *scbbase; 33 unsigned int wide; 34 unsigned int channel_in; 35 unsigned int channel_out; 36 unsigned int lines_in; 37 unsigned int lines_out; 38 struct s6000_pcm_dma_params dma_params; 39}; 40 41#define S6_I2S_INTERRUPT_STATUS 0x00 42#define S6_I2S_INT_OVERRUN 1 43#define S6_I2S_INT_UNDERRUN 2 44#define S6_I2S_INT_ALIGNMENT 4 45#define S6_I2S_INTERRUPT_ENABLE 0x04 46#define S6_I2S_INTERRUPT_RAW 0x08 47#define S6_I2S_INTERRUPT_CLEAR 0x0C 48#define S6_I2S_INTERRUPT_SET 0x10 49#define S6_I2S_MODE 0x20 50#define S6_I2S_DUAL 0 51#define S6_I2S_WIDE 1 52#define S6_I2S_TX_DEFAULT 0x24 53#define S6_I2S_DATA_CFG(c) (0x40 + 0x10 * (c)) 54#define S6_I2S_IN 0 55#define S6_I2S_OUT 1 56#define S6_I2S_UNUSED 2 57#define S6_I2S_INTERFACE_CFG(c) (0x44 + 0x10 * (c)) 58#define S6_I2S_DIV_MASK 0x001fff 59#define S6_I2S_16BIT 0x000000 60#define S6_I2S_20BIT 0x002000 61#define S6_I2S_24BIT 0x004000 62#define S6_I2S_32BIT 0x006000 63#define S6_I2S_BITS_MASK 0x006000 64#define S6_I2S_MEM_16BIT 0x000000 65#define S6_I2S_MEM_32BIT 0x008000 66#define S6_I2S_MEM_MASK 0x008000 67#define S6_I2S_CHANNELS_SHIFT 16 68#define S6_I2S_CHANNELS_MASK 0x030000 69#define S6_I2S_SCK_IN 0x000000 70#define S6_I2S_SCK_OUT 0x040000 71#define S6_I2S_SCK_DIR 0x040000 72#define S6_I2S_WS_IN 0x000000 73#define S6_I2S_WS_OUT 0x080000 74#define S6_I2S_WS_DIR 0x080000 75#define S6_I2S_LEFT_FIRST 0x000000 76#define S6_I2S_RIGHT_FIRST 0x100000 77#define S6_I2S_FIRST 0x100000 78#define S6_I2S_CUR_SCK 0x200000 79#define S6_I2S_CUR_WS 0x400000 80#define S6_I2S_ENABLE(c) (0x48 + 0x10 * (c)) 81#define S6_I2S_DISABLE_IF 0x02 82#define S6_I2S_ENABLE_IF 0x03 83#define S6_I2S_IS_BUSY 0x04 84#define S6_I2S_DMA_ACTIVE 0x08 85#define S6_I2S_IS_ENABLED 0x10 86 87#define S6_I2S_NUM_LINES 4 88 89#define S6_I2S_SIF_PORT0 0x0000000 90#define S6_I2S_SIF_PORT1 0x0000080 /* docs say 0x0000010 */ 91 92static inline void s6_i2s_write_reg(struct s6000_i2s_dev *dev, int reg, u32 val) 93{ 94 writel(val, dev->scbbase + reg); 95} 96 97static inline u32 s6_i2s_read_reg(struct s6000_i2s_dev *dev, int reg) 98{ 99 return readl(dev->scbbase + reg); 100} 101 102static inline void s6_i2s_mod_reg(struct s6000_i2s_dev *dev, int reg, 103 u32 mask, u32 val) 104{ 105 val ^= s6_i2s_read_reg(dev, reg) & ~mask; 106 s6_i2s_write_reg(dev, reg, val); 107} 108 109static void s6000_i2s_start_channel(struct s6000_i2s_dev *dev, int channel) 110{ 111 int i, j, cur, prev; 112 113 /* 114 * Wait for WCLK to toggle 5 times before enabling the channel 115 * s6000 Family Datasheet 3.6.4: 116 * "At least two cycles of WS must occur between commands 117 * to disable or enable the interface" 118 */ 119 j = 0; 120 prev = ~S6_I2S_CUR_WS; 121 for (i = 1000000; --i && j < 6; ) { 122 cur = s6_i2s_read_reg(dev, S6_I2S_INTERFACE_CFG(channel)) 123 & S6_I2S_CUR_WS; 124 if (prev != cur) { 125 prev = cur; 126 j++; 127 } 128 } 129 if (j < 6) 130 printk(KERN_WARNING "s6000-i2s: timeout waiting for WCLK\n"); 131 132 s6_i2s_write_reg(dev, S6_I2S_ENABLE(channel), S6_I2S_ENABLE_IF); 133} 134 135static void s6000_i2s_stop_channel(struct s6000_i2s_dev *dev, int channel) 136{ 137 s6_i2s_write_reg(dev, S6_I2S_ENABLE(channel), S6_I2S_DISABLE_IF); 138} 139 140static void s6000_i2s_start(struct snd_pcm_substream *substream) 141{ 142 struct snd_soc_pcm_runtime *rtd = substream->private_data; 143 struct s6000_i2s_dev *dev = snd_soc_dai_get_drvdata(rtd->cpu_dai); 144 int channel; 145 146 channel = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 147 dev->channel_out : dev->channel_in; 148 149 s6000_i2s_start_channel(dev, channel); 150} 151 152static void s6000_i2s_stop(struct snd_pcm_substream *substream) 153{ 154 struct snd_soc_pcm_runtime *rtd = substream->private_data; 155 struct s6000_i2s_dev *dev = snd_soc_dai_get_drvdata(rtd->cpu_dai); 156 int channel; 157 158 channel = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 159 dev->channel_out : dev->channel_in; 160 161 s6000_i2s_stop_channel(dev, channel); 162} 163 164static int s6000_i2s_trigger(struct snd_pcm_substream *substream, int cmd, 165 int after) 166{ 167 switch (cmd) { 168 case SNDRV_PCM_TRIGGER_START: 169 case SNDRV_PCM_TRIGGER_RESUME: 170 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 171 if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE) ^ !after) 172 s6000_i2s_start(substream); 173 break; 174 case SNDRV_PCM_TRIGGER_STOP: 175 case SNDRV_PCM_TRIGGER_SUSPEND: 176 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 177 if (!after) 178 s6000_i2s_stop(substream); 179 } 180 return 0; 181} 182 183static unsigned int s6000_i2s_int_sources(struct s6000_i2s_dev *dev) 184{ 185 unsigned int pending; 186 pending = s6_i2s_read_reg(dev, S6_I2S_INTERRUPT_RAW); 187 pending &= S6_I2S_INT_ALIGNMENT | 188 S6_I2S_INT_UNDERRUN | 189 S6_I2S_INT_OVERRUN; 190 s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_CLEAR, pending); 191 192 return pending; 193} 194 195static unsigned int s6000_i2s_check_xrun(struct snd_soc_dai *cpu_dai) 196{ 197 struct s6000_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); 198 unsigned int errors; 199 unsigned int ret; 200 201 errors = s6000_i2s_int_sources(dev); 202 if (likely(!errors)) 203 return 0; 204 205 ret = 0; 206 if (errors & S6_I2S_INT_ALIGNMENT) 207 printk(KERN_ERR "s6000-i2s: WCLK misaligned\n"); 208 if (errors & S6_I2S_INT_UNDERRUN) 209 ret |= 1 << SNDRV_PCM_STREAM_PLAYBACK; 210 if (errors & S6_I2S_INT_OVERRUN) 211 ret |= 1 << SNDRV_PCM_STREAM_CAPTURE; 212 return ret; 213} 214 215static void s6000_i2s_wait_disabled(struct s6000_i2s_dev *dev) 216{ 217 int channel; 218 int n = 50; 219 for (channel = 0; channel < 2; channel++) { 220 while (--n >= 0) { 221 int v = s6_i2s_read_reg(dev, S6_I2S_ENABLE(channel)); 222 if ((v & S6_I2S_IS_ENABLED) 223 || !(v & (S6_I2S_DMA_ACTIVE | S6_I2S_IS_BUSY))) 224 break; 225 udelay(20); 226 } 227 } 228 if (n < 0) 229 printk(KERN_WARNING "s6000-i2s: timeout disabling interfaces"); 230} 231 232static int s6000_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, 233 unsigned int fmt) 234{ 235 struct s6000_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); 236 u32 w; 237 238 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 239 case SND_SOC_DAIFMT_CBM_CFM: 240 w = S6_I2S_SCK_IN | S6_I2S_WS_IN; 241 break; 242 case SND_SOC_DAIFMT_CBS_CFM: 243 w = S6_I2S_SCK_OUT | S6_I2S_WS_IN; 244 break; 245 case SND_SOC_DAIFMT_CBM_CFS: 246 w = S6_I2S_SCK_IN | S6_I2S_WS_OUT; 247 break; 248 case SND_SOC_DAIFMT_CBS_CFS: 249 w = S6_I2S_SCK_OUT | S6_I2S_WS_OUT; 250 break; 251 default: 252 return -EINVAL; 253 } 254 255 switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 256 case SND_SOC_DAIFMT_NB_NF: 257 w |= S6_I2S_LEFT_FIRST; 258 break; 259 case SND_SOC_DAIFMT_NB_IF: 260 w |= S6_I2S_RIGHT_FIRST; 261 break; 262 default: 263 return -EINVAL; 264 } 265 266 s6_i2s_mod_reg(dev, S6_I2S_INTERFACE_CFG(0), 267 S6_I2S_FIRST | S6_I2S_WS_DIR | S6_I2S_SCK_DIR, w); 268 s6_i2s_mod_reg(dev, S6_I2S_INTERFACE_CFG(1), 269 S6_I2S_FIRST | S6_I2S_WS_DIR | S6_I2S_SCK_DIR, w); 270 271 return 0; 272} 273 274static int s6000_i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div) 275{ 276 struct s6000_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); 277 278 if (!div || (div & 1) || div > (S6_I2S_DIV_MASK + 1) * 2) 279 return -EINVAL; 280 281 s6_i2s_mod_reg(dev, S6_I2S_INTERFACE_CFG(div_id), 282 S6_I2S_DIV_MASK, div / 2 - 1); 283 return 0; 284} 285 286static int s6000_i2s_hw_params(struct snd_pcm_substream *substream, 287 struct snd_pcm_hw_params *params, 288 struct snd_soc_dai *dai) 289{ 290 struct s6000_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); 291 int interf; 292 u32 w = 0; 293 294 if (dev->wide) 295 interf = 0; 296 else { 297 w |= (((params_channels(params) - 2) / 2) 298 << S6_I2S_CHANNELS_SHIFT) & S6_I2S_CHANNELS_MASK; 299 interf = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 300 ? dev->channel_out : dev->channel_in; 301 } 302 303 switch (params_format(params)) { 304 case SNDRV_PCM_FORMAT_S16_LE: 305 w |= S6_I2S_16BIT | S6_I2S_MEM_16BIT; 306 break; 307 case SNDRV_PCM_FORMAT_S32_LE: 308 w |= S6_I2S_32BIT | S6_I2S_MEM_32BIT; 309 break; 310 default: 311 printk(KERN_WARNING "s6000-i2s: unsupported PCM format %x\n", 312 params_format(params)); 313 return -EINVAL; 314 } 315 316 if (s6_i2s_read_reg(dev, S6_I2S_INTERFACE_CFG(interf)) 317 & S6_I2S_IS_ENABLED) { 318 printk(KERN_ERR "s6000-i2s: interface already enabled\n"); 319 return -EBUSY; 320 } 321 322 s6_i2s_mod_reg(dev, S6_I2S_INTERFACE_CFG(interf), 323 S6_I2S_CHANNELS_MASK|S6_I2S_MEM_MASK|S6_I2S_BITS_MASK, 324 w); 325 326 return 0; 327} 328 329static int s6000_i2s_dai_probe(struct snd_soc_dai *dai) 330{ 331 struct s6000_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); 332 struct s6000_snd_platform_data *pdata = dai->dev->platform_data; 333 334 if (!pdata) 335 return -EINVAL; 336 337 dai->capture_dma_data = &dev->dma_params; 338 dai->playback_dma_data = &dev->dma_params; 339 340 dev->wide = pdata->wide; 341 dev->channel_in = pdata->channel_in; 342 dev->channel_out = pdata->channel_out; 343 dev->lines_in = pdata->lines_in; 344 dev->lines_out = pdata->lines_out; 345 346 s6_i2s_write_reg(dev, S6_I2S_MODE, 347 dev->wide ? S6_I2S_WIDE : S6_I2S_DUAL); 348 349 if (dev->wide) { 350 int i; 351 352 if (dev->lines_in + dev->lines_out > S6_I2S_NUM_LINES) 353 return -EINVAL; 354 355 dev->channel_in = 0; 356 dev->channel_out = 1; 357 dai->driver->capture.channels_min = 2 * dev->lines_in; 358 dai->driver->capture.channels_max = dai->driver->capture.channels_min; 359 dai->driver->playback.channels_min = 2 * dev->lines_out; 360 dai->driver->playback.channels_max = dai->driver->playback.channels_min; 361 362 for (i = 0; i < dev->lines_out; i++) 363 s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(i), S6_I2S_OUT); 364 365 for (; i < S6_I2S_NUM_LINES - dev->lines_in; i++) 366 s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(i), 367 S6_I2S_UNUSED); 368 369 for (; i < S6_I2S_NUM_LINES; i++) 370 s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(i), S6_I2S_IN); 371 } else { 372 unsigned int cfg[2] = {S6_I2S_UNUSED, S6_I2S_UNUSED}; 373 374 if (dev->lines_in > 1 || dev->lines_out > 1) 375 return -EINVAL; 376 377 dai->driver->capture.channels_min = 2 * dev->lines_in; 378 dai->driver->capture.channels_max = 8 * dev->lines_in; 379 dai->driver->playback.channels_min = 2 * dev->lines_out; 380 dai->driver->playback.channels_max = 8 * dev->lines_out; 381 382 if (dev->lines_in) 383 cfg[dev->channel_in] = S6_I2S_IN; 384 if (dev->lines_out) 385 cfg[dev->channel_out] = S6_I2S_OUT; 386 387 s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(0), cfg[0]); 388 s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(1), cfg[1]); 389 } 390 391 if (dev->lines_out) { 392 if (dev->lines_in) { 393 if (!dev->dma_params.dma_out) 394 return -ENODEV; 395 } else { 396 dev->dma_params.dma_out = dev->dma_params.dma_in; 397 dev->dma_params.dma_in = 0; 398 } 399 } 400 dev->dma_params.sif_in = dev->sifbase + (dev->channel_in ? 401 S6_I2S_SIF_PORT1 : S6_I2S_SIF_PORT0); 402 dev->dma_params.sif_out = dev->sifbase + (dev->channel_out ? 403 S6_I2S_SIF_PORT1 : S6_I2S_SIF_PORT0); 404 dev->dma_params.same_rate = pdata->same_rate | pdata->wide; 405 return 0; 406} 407 408#define S6000_I2S_RATES (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_5512 | \ 409 SNDRV_PCM_RATE_8000_192000) 410#define S6000_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) 411 412static struct snd_soc_dai_ops s6000_i2s_dai_ops = { 413 .set_fmt = s6000_i2s_set_dai_fmt, 414 .set_clkdiv = s6000_i2s_set_clkdiv, 415 .hw_params = s6000_i2s_hw_params, 416}; 417 418static struct snd_soc_dai_driver s6000_i2s_dai = { 419 .probe = s6000_i2s_dai_probe, 420 .playback = { 421 .channels_min = 2, 422 .channels_max = 8, 423 .formats = S6000_I2S_FORMATS, 424 .rates = S6000_I2S_RATES, 425 .rate_min = 0, 426 .rate_max = 1562500, 427 }, 428 .capture = { 429 .channels_min = 2, 430 .channels_max = 8, 431 .formats = S6000_I2S_FORMATS, 432 .rates = S6000_I2S_RATES, 433 .rate_min = 0, 434 .rate_max = 1562500, 435 }, 436 .ops = &s6000_i2s_dai_ops, 437}; 438 439static int __devinit s6000_i2s_probe(struct platform_device *pdev) 440{ 441 struct s6000_i2s_dev *dev; 442 struct resource *scbmem, *sifmem, *region, *dma1, *dma2; 443 u8 __iomem *mmio; 444 int ret; 445 446 scbmem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 447 if (!scbmem) { 448 dev_err(&pdev->dev, "no mem resource?\n"); 449 ret = -ENODEV; 450 goto err_release_none; 451 } 452 453 region = request_mem_region(scbmem->start, resource_size(scbmem), 454 pdev->name); 455 if (!region) { 456 dev_err(&pdev->dev, "I2S SCB region already claimed\n"); 457 ret = -EBUSY; 458 goto err_release_none; 459 } 460 461 mmio = ioremap(scbmem->start, resource_size(scbmem)); 462 if (!mmio) { 463 dev_err(&pdev->dev, "can't ioremap SCB region\n"); 464 ret = -ENOMEM; 465 goto err_release_scb; 466 } 467 468 sifmem = platform_get_resource(pdev, IORESOURCE_MEM, 1); 469 if (!sifmem) { 470 dev_err(&pdev->dev, "no second mem resource?\n"); 471 ret = -ENODEV; 472 goto err_release_map; 473 } 474 475 region = request_mem_region(sifmem->start, resource_size(sifmem), 476 pdev->name); 477 if (!region) { 478 dev_err(&pdev->dev, "I2S SIF region already claimed\n"); 479 ret = -EBUSY; 480 goto err_release_map; 481 } 482 483 dma1 = platform_get_resource(pdev, IORESOURCE_DMA, 0); 484 if (!dma1) { 485 dev_err(&pdev->dev, "no dma resource?\n"); 486 ret = -ENODEV; 487 goto err_release_sif; 488 } 489 490 region = request_mem_region(dma1->start, resource_size(dma1), 491 pdev->name); 492 if (!region) { 493 dev_err(&pdev->dev, "I2S DMA region already claimed\n"); 494 ret = -EBUSY; 495 goto err_release_sif; 496 } 497 498 dma2 = platform_get_resource(pdev, IORESOURCE_DMA, 1); 499 if (dma2) { 500 region = request_mem_region(dma2->start, resource_size(dma2), 501 pdev->name); 502 if (!region) { 503 dev_err(&pdev->dev, 504 "I2S DMA region already claimed\n"); 505 ret = -EBUSY; 506 goto err_release_dma1; 507 } 508 } 509 510 dev = kzalloc(sizeof(struct s6000_i2s_dev), GFP_KERNEL); 511 if (!dev) { 512 ret = -ENOMEM; 513 goto err_release_dma2; 514 } 515 dev_set_drvdata(&pdev->dev, dev); 516 517 dev->sifbase = sifmem->start; 518 dev->scbbase = mmio; 519 520 s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_ENABLE, 0); 521 s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_CLEAR, 522 S6_I2S_INT_ALIGNMENT | 523 S6_I2S_INT_UNDERRUN | 524 S6_I2S_INT_OVERRUN); 525 526 s6000_i2s_stop_channel(dev, 0); 527 s6000_i2s_stop_channel(dev, 1); 528 s6000_i2s_wait_disabled(dev); 529 530 dev->dma_params.check_xrun = s6000_i2s_check_xrun; 531 dev->dma_params.trigger = s6000_i2s_trigger; 532 dev->dma_params.dma_in = dma1->start; 533 dev->dma_params.dma_out = dma2 ? dma2->start : 0; 534 dev->dma_params.irq = platform_get_irq(pdev, 0); 535 if (dev->dma_params.irq < 0) { 536 dev_err(&pdev->dev, "no irq resource?\n"); 537 ret = -ENODEV; 538 goto err_release_dev; 539 } 540 541 s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_ENABLE, 542 S6_I2S_INT_ALIGNMENT | 543 S6_I2S_INT_UNDERRUN | 544 S6_I2S_INT_OVERRUN); 545 546 ret = snd_soc_register_dai(&pdev->dev, &s6000_i2s_dai); 547 if (ret) 548 goto err_release_dev; 549 550 return 0; 551 552err_release_dev: 553 kfree(dev); 554err_release_dma2: 555 if (dma2) 556 release_mem_region(dma2->start, resource_size(dma2)); 557err_release_dma1: 558 release_mem_region(dma1->start, resource_size(dma1)); 559err_release_sif: 560 release_mem_region(sifmem->start, resource_size(sifmem)); 561err_release_map: 562 iounmap(mmio); 563err_release_scb: 564 release_mem_region(scbmem->start, resource_size(scbmem)); 565err_release_none: 566 return ret; 567} 568 569static void __devexit s6000_i2s_remove(struct platform_device *pdev) 570{ 571 struct s6000_i2s_dev *dev = dev_get_drvdata(&pdev->dev); 572 struct resource *region; 573 void __iomem *mmio = dev->scbbase; 574 575 snd_soc_unregister_dai(&pdev->dev); 576 577 s6000_i2s_stop_channel(dev, 0); 578 s6000_i2s_stop_channel(dev, 1); 579 580 s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_ENABLE, 0); 581 kfree(dev); 582 583 region = platform_get_resource(pdev, IORESOURCE_DMA, 0); 584 release_mem_region(region->start, resource_size(region)); 585 586 region = platform_get_resource(pdev, IORESOURCE_DMA, 1); 587 if (region) 588 release_mem_region(region->start, resource_size(region)); 589 590 region = platform_get_resource(pdev, IORESOURCE_MEM, 0); 591 release_mem_region(region->start, resource_size(region)); 592 593 iounmap(mmio); 594 region = platform_get_resource(pdev, IORESOURCE_IO, 0); 595 release_mem_region(region->start, resource_size(region)); 596} 597 598static struct platform_driver s6000_i2s_driver = { 599 .probe = s6000_i2s_probe, 600 .remove = __devexit_p(s6000_i2s_remove), 601 .driver = { 602 .name = "s6000-i2s", 603 .owner = THIS_MODULE, 604 }, 605}; 606 607static int __init s6000_i2s_init(void) 608{ 609 return platform_driver_register(&s6000_i2s_driver); 610} 611module_init(s6000_i2s_init); 612 613static void __exit s6000_i2s_exit(void) 614{ 615 platform_driver_unregister(&s6000_i2s_driver); 616} 617module_exit(s6000_i2s_exit); 618 619MODULE_AUTHOR("Daniel Gloeckner"); 620MODULE_DESCRIPTION("Stretch s6000 family I2S SoC Interface"); 621MODULE_LICENSE("GPL"); 622