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