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 = rtd->dai->cpu_dai->private_data; 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 = rtd->dai->cpu_dai->private_data; 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 = cpu_dai->private_data; 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 = cpu_dai->private_data; 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 = dai->private_data; 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 = dai->private_data; 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 platform_device *pdev, 330 struct snd_soc_dai *dai) 331{ 332 struct s6000_i2s_dev *dev = dai->private_data; 333 struct s6000_snd_platform_data *pdata = pdev->dev.platform_data; 334 335 if (!pdata) 336 return -EINVAL; 337 338 dev->wide = pdata->wide; 339 dev->channel_in = pdata->channel_in; 340 dev->channel_out = pdata->channel_out; 341 dev->lines_in = pdata->lines_in; 342 dev->lines_out = pdata->lines_out; 343 344 s6_i2s_write_reg(dev, S6_I2S_MODE, 345 dev->wide ? S6_I2S_WIDE : S6_I2S_DUAL); 346 347 if (dev->wide) { 348 int i; 349 350 if (dev->lines_in + dev->lines_out > S6_I2S_NUM_LINES) 351 return -EINVAL; 352 353 dev->channel_in = 0; 354 dev->channel_out = 1; 355 dai->capture.channels_min = 2 * dev->lines_in; 356 dai->capture.channels_max = dai->capture.channels_min; 357 dai->playback.channels_min = 2 * dev->lines_out; 358 dai->playback.channels_max = dai->playback.channels_min; 359 360 for (i = 0; i < dev->lines_out; i++) 361 s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(i), S6_I2S_OUT); 362 363 for (; i < S6_I2S_NUM_LINES - dev->lines_in; i++) 364 s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(i), 365 S6_I2S_UNUSED); 366 367 for (; i < S6_I2S_NUM_LINES; i++) 368 s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(i), S6_I2S_IN); 369 } else { 370 unsigned int cfg[2] = {S6_I2S_UNUSED, S6_I2S_UNUSED}; 371 372 if (dev->lines_in > 1 || dev->lines_out > 1) 373 return -EINVAL; 374 375 dai->capture.channels_min = 2 * dev->lines_in; 376 dai->capture.channels_max = 8 * dev->lines_in; 377 dai->playback.channels_min = 2 * dev->lines_out; 378 dai->playback.channels_max = 8 * dev->lines_out; 379 380 if (dev->lines_in) 381 cfg[dev->channel_in] = S6_I2S_IN; 382 if (dev->lines_out) 383 cfg[dev->channel_out] = S6_I2S_OUT; 384 385 s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(0), cfg[0]); 386 s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(1), cfg[1]); 387 } 388 389 if (dev->lines_out) { 390 if (dev->lines_in) { 391 if (!dev->dma_params.dma_out) 392 return -ENODEV; 393 } else { 394 dev->dma_params.dma_out = dev->dma_params.dma_in; 395 dev->dma_params.dma_in = 0; 396 } 397 } 398 dev->dma_params.sif_in = dev->sifbase + (dev->channel_in ? 399 S6_I2S_SIF_PORT1 : S6_I2S_SIF_PORT0); 400 dev->dma_params.sif_out = dev->sifbase + (dev->channel_out ? 401 S6_I2S_SIF_PORT1 : S6_I2S_SIF_PORT0); 402 dev->dma_params.same_rate = pdata->same_rate | pdata->wide; 403 return 0; 404} 405 406#define S6000_I2S_RATES (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_5512 | \ 407 SNDRV_PCM_RATE_8000_192000) 408#define S6000_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) 409 410static struct snd_soc_dai_ops s6000_i2s_dai_ops = { 411 .set_fmt = s6000_i2s_set_dai_fmt, 412 .set_clkdiv = s6000_i2s_set_clkdiv, 413 .hw_params = s6000_i2s_hw_params, 414}; 415 416struct snd_soc_dai s6000_i2s_dai = { 417 .name = "s6000-i2s", 418 .id = 0, 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} 438EXPORT_SYMBOL_GPL(s6000_i2s_dai); 439 440static int __devinit s6000_i2s_probe(struct platform_device *pdev) 441{ 442 struct s6000_i2s_dev *dev; 443 struct resource *scbmem, *sifmem, *region, *dma1, *dma2; 444 u8 __iomem *mmio; 445 int ret; 446 447 scbmem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 448 if (!scbmem) { 449 dev_err(&pdev->dev, "no mem resource?\n"); 450 ret = -ENODEV; 451 goto err_release_none; 452 } 453 454 region = request_mem_region(scbmem->start, resource_size(scbmem), 455 pdev->name); 456 if (!region) { 457 dev_err(&pdev->dev, "I2S SCB region already claimed\n"); 458 ret = -EBUSY; 459 goto err_release_none; 460 } 461 462 mmio = ioremap(scbmem->start, resource_size(scbmem)); 463 if (!mmio) { 464 dev_err(&pdev->dev, "can't ioremap SCB region\n"); 465 ret = -ENOMEM; 466 goto err_release_scb; 467 } 468 469 sifmem = platform_get_resource(pdev, IORESOURCE_MEM, 1); 470 if (!sifmem) { 471 dev_err(&pdev->dev, "no second mem resource?\n"); 472 ret = -ENODEV; 473 goto err_release_map; 474 } 475 476 region = request_mem_region(sifmem->start, resource_size(sifmem), 477 pdev->name); 478 if (!region) { 479 dev_err(&pdev->dev, "I2S SIF region already claimed\n"); 480 ret = -EBUSY; 481 goto err_release_map; 482 } 483 484 dma1 = platform_get_resource(pdev, IORESOURCE_DMA, 0); 485 if (!dma1) { 486 dev_err(&pdev->dev, "no dma resource?\n"); 487 ret = -ENODEV; 488 goto err_release_sif; 489 } 490 491 region = request_mem_region(dma1->start, resource_size(dma1), 492 pdev->name); 493 if (!region) { 494 dev_err(&pdev->dev, "I2S DMA region already claimed\n"); 495 ret = -EBUSY; 496 goto err_release_sif; 497 } 498 499 dma2 = platform_get_resource(pdev, IORESOURCE_DMA, 1); 500 if (dma2) { 501 region = request_mem_region(dma2->start, resource_size(dma2), 502 pdev->name); 503 if (!region) { 504 dev_err(&pdev->dev, 505 "I2S DMA region already claimed\n"); 506 ret = -EBUSY; 507 goto err_release_dma1; 508 } 509 } 510 511 dev = kzalloc(sizeof(struct s6000_i2s_dev), GFP_KERNEL); 512 if (!dev) { 513 ret = -ENOMEM; 514 goto err_release_dma2; 515 } 516 517 s6000_i2s_dai.dev = &pdev->dev; 518 s6000_i2s_dai.private_data = dev; 519 s6000_i2s_dai.capture.dma_data = &dev->dma_params; 520 s6000_i2s_dai.playback.dma_data = &dev->dma_params; 521 522 dev->sifbase = sifmem->start; 523 dev->scbbase = mmio; 524 525 s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_ENABLE, 0); 526 s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_CLEAR, 527 S6_I2S_INT_ALIGNMENT | 528 S6_I2S_INT_UNDERRUN | 529 S6_I2S_INT_OVERRUN); 530 531 s6000_i2s_stop_channel(dev, 0); 532 s6000_i2s_stop_channel(dev, 1); 533 s6000_i2s_wait_disabled(dev); 534 535 dev->dma_params.check_xrun = s6000_i2s_check_xrun; 536 dev->dma_params.trigger = s6000_i2s_trigger; 537 dev->dma_params.dma_in = dma1->start; 538 dev->dma_params.dma_out = dma2 ? dma2->start : 0; 539 dev->dma_params.irq = platform_get_irq(pdev, 0); 540 if (dev->dma_params.irq < 0) { 541 dev_err(&pdev->dev, "no irq resource?\n"); 542 ret = -ENODEV; 543 goto err_release_dev; 544 } 545 546 s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_ENABLE, 547 S6_I2S_INT_ALIGNMENT | 548 S6_I2S_INT_UNDERRUN | 549 S6_I2S_INT_OVERRUN); 550 551 ret = snd_soc_register_dai(&s6000_i2s_dai); 552 if (ret) 553 goto err_release_dev; 554 555 return 0; 556 557err_release_dev: 558 kfree(dev); 559err_release_dma2: 560 if (dma2) 561 release_mem_region(dma2->start, resource_size(dma2)); 562err_release_dma1: 563 release_mem_region(dma1->start, resource_size(dma1)); 564err_release_sif: 565 release_mem_region(sifmem->start, resource_size(sifmem)); 566err_release_map: 567 iounmap(mmio); 568err_release_scb: 569 release_mem_region(scbmem->start, resource_size(scbmem)); 570err_release_none: 571 return ret; 572} 573 574static void __devexit s6000_i2s_remove(struct platform_device *pdev) 575{ 576 struct s6000_i2s_dev *dev = s6000_i2s_dai.private_data; 577 struct resource *region; 578 void __iomem *mmio = dev->scbbase; 579 580 snd_soc_unregister_dai(&s6000_i2s_dai); 581 582 s6000_i2s_stop_channel(dev, 0); 583 s6000_i2s_stop_channel(dev, 1); 584 585 s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_ENABLE, 0); 586 s6000_i2s_dai.private_data = 0; 587 kfree(dev); 588 589 region = platform_get_resource(pdev, IORESOURCE_DMA, 0); 590 release_mem_region(region->start, resource_size(region)); 591 592 region = platform_get_resource(pdev, IORESOURCE_DMA, 1); 593 if (region) 594 release_mem_region(region->start, resource_size(region)); 595 596 region = platform_get_resource(pdev, IORESOURCE_MEM, 0); 597 release_mem_region(region->start, resource_size(region)); 598 599 iounmap(mmio); 600 region = platform_get_resource(pdev, IORESOURCE_IO, 0); 601 release_mem_region(region->start, resource_size(region)); 602} 603 604static struct platform_driver s6000_i2s_driver = { 605 .probe = s6000_i2s_probe, 606 .remove = __devexit_p(s6000_i2s_remove), 607 .driver = { 608 .name = "s6000-i2s", 609 .owner = THIS_MODULE, 610 }, 611}; 612 613static int __init s6000_i2s_init(void) 614{ 615 return platform_driver_register(&s6000_i2s_driver); 616} 617module_init(s6000_i2s_init); 618 619static void __exit s6000_i2s_exit(void) 620{ 621 platform_driver_unregister(&s6000_i2s_driver); 622} 623module_exit(s6000_i2s_exit); 624 625MODULE_AUTHOR("Daniel Gloeckner"); 626MODULE_DESCRIPTION("Stretch s6000 family I2S SoC Interface"); 627MODULE_LICENSE("GPL"); 628