linux/sound/soc/pxa/pxa2xx-i2s.c
<<
>>
Prefs
   1/*
   2 * pxa2xx-i2s.c  --  ALSA Soc Audio Layer
   3 *
   4 * Copyright 2005 Wolfson Microelectronics PLC.
   5 * Author: Liam Girdwood
   6 *         lrg@slimlogic.co.uk
   7 *
   8 *  This program is free software; you can redistribute  it and/or modify it
   9 *  under  the terms of  the GNU General  Public License as published by the
  10 *  Free Software Foundation;  either version 2 of the  License, or (at your
  11 *  option) any later version.
  12 */
  13
  14#include <linux/init.h>
  15#include <linux/module.h>
  16#include <linux/device.h>
  17#include <linux/delay.h>
  18#include <linux/clk.h>
  19#include <linux/platform_device.h>
  20#include <linux/io.h>
  21#include <sound/core.h>
  22#include <sound/pcm.h>
  23#include <sound/initval.h>
  24#include <sound/soc.h>
  25#include <sound/pxa2xx-lib.h>
  26#include <sound/dmaengine_pcm.h>
  27
  28#include <mach/hardware.h>
  29#include <mach/audio.h>
  30
  31#include "pxa2xx-i2s.h"
  32
  33/*
  34 * I2S Controller Register and Bit Definitions
  35 */
  36#define SACR0           __REG(0x40400000)  /* Global Control Register */
  37#define SACR1           __REG(0x40400004)  /* Serial Audio I 2 S/MSB-Justified Control Register */
  38#define SASR0           __REG(0x4040000C)  /* Serial Audio I 2 S/MSB-Justified Interface and FIFO Status Register */
  39#define SAIMR           __REG(0x40400014)  /* Serial Audio Interrupt Mask Register */
  40#define SAICR           __REG(0x40400018)  /* Serial Audio Interrupt Clear Register */
  41#define SADIV           __REG(0x40400060)  /* Audio Clock Divider Register. */
  42#define SADR            __REG(0x40400080)  /* Serial Audio Data Register (TX and RX FIFO access Register). */
  43
  44#define SACR0_RFTH(x)   ((x) << 12)     /* Rx FIFO Interrupt or DMA Trigger Threshold */
  45#define SACR0_TFTH(x)   ((x) << 8)      /* Tx FIFO Interrupt or DMA Trigger Threshold */
  46#define SACR0_STRF      (1 << 5)        /* FIFO Select for EFWR Special Function */
  47#define SACR0_EFWR      (1 << 4)        /* Enable EFWR Function  */
  48#define SACR0_RST       (1 << 3)        /* FIFO, i2s Register Reset */
  49#define SACR0_BCKD      (1 << 2)        /* Bit Clock Direction */
  50#define SACR0_ENB       (1 << 0)        /* Enable I2S Link */
  51#define SACR1_ENLBF     (1 << 5)        /* Enable Loopback */
  52#define SACR1_DRPL      (1 << 4)        /* Disable Replaying Function */
  53#define SACR1_DREC      (1 << 3)        /* Disable Recording Function */
  54#define SACR1_AMSL      (1 << 0)        /* Specify Alternate Mode */
  55
  56#define SASR0_I2SOFF    (1 << 7)        /* Controller Status */
  57#define SASR0_ROR       (1 << 6)        /* Rx FIFO Overrun */
  58#define SASR0_TUR       (1 << 5)        /* Tx FIFO Underrun */
  59#define SASR0_RFS       (1 << 4)        /* Rx FIFO Service Request */
  60#define SASR0_TFS       (1 << 3)        /* Tx FIFO Service Request */
  61#define SASR0_BSY       (1 << 2)        /* I2S Busy */
  62#define SASR0_RNE       (1 << 1)        /* Rx FIFO Not Empty */
  63#define SASR0_TNF       (1 << 0)        /* Tx FIFO Not Empty */
  64
  65#define SAICR_ROR       (1 << 6)        /* Clear Rx FIFO Overrun Interrupt */
  66#define SAICR_TUR       (1 << 5)        /* Clear Tx FIFO Underrun Interrupt */
  67
  68#define SAIMR_ROR       (1 << 6)        /* Enable Rx FIFO Overrun Condition Interrupt */
  69#define SAIMR_TUR       (1 << 5)        /* Enable Tx FIFO Underrun Condition Interrupt */
  70#define SAIMR_RFS       (1 << 4)        /* Enable Rx FIFO Service Interrupt */
  71#define SAIMR_TFS       (1 << 3)        /* Enable Tx FIFO Service Interrupt */
  72
  73struct pxa_i2s_port {
  74        u32 sadiv;
  75        u32 sacr0;
  76        u32 sacr1;
  77        u32 saimr;
  78        int master;
  79        u32 fmt;
  80};
  81static struct pxa_i2s_port pxa_i2s;
  82static struct clk *clk_i2s;
  83static int clk_ena = 0;
  84
  85static unsigned long pxa2xx_i2s_pcm_stereo_out_req = 3;
  86static struct snd_dmaengine_dai_dma_data pxa2xx_i2s_pcm_stereo_out = {
  87        .addr           = __PREG(SADR),
  88        .addr_width     = DMA_SLAVE_BUSWIDTH_4_BYTES,
  89        .maxburst       = 32,
  90        .filter_data    = &pxa2xx_i2s_pcm_stereo_out_req,
  91};
  92
  93static unsigned long pxa2xx_i2s_pcm_stereo_in_req = 2;
  94static struct snd_dmaengine_dai_dma_data pxa2xx_i2s_pcm_stereo_in = {
  95        .addr           = __PREG(SADR),
  96        .addr_width     = DMA_SLAVE_BUSWIDTH_4_BYTES,
  97        .maxburst       = 32,
  98        .filter_data    = &pxa2xx_i2s_pcm_stereo_in_req,
  99};
 100
 101static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream,
 102                              struct snd_soc_dai *dai)
 103{
 104        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 105        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 106
 107        if (IS_ERR(clk_i2s))
 108                return PTR_ERR(clk_i2s);
 109
 110        if (!cpu_dai->active)
 111                SACR0 = 0;
 112
 113        return 0;
 114}
 115
 116/* wait for I2S controller to be ready */
 117static int pxa_i2s_wait(void)
 118{
 119        int i;
 120
 121        /* flush the Rx FIFO */
 122        for(i = 0; i < 16; i++)
 123                SADR;
 124        return 0;
 125}
 126
 127static int pxa2xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
 128                unsigned int fmt)
 129{
 130        /* interface format */
 131        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 132        case SND_SOC_DAIFMT_I2S:
 133                pxa_i2s.fmt = 0;
 134                break;
 135        case SND_SOC_DAIFMT_LEFT_J:
 136                pxa_i2s.fmt = SACR1_AMSL;
 137                break;
 138        }
 139
 140        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 141        case SND_SOC_DAIFMT_CBS_CFS:
 142                pxa_i2s.master = 1;
 143                break;
 144        case SND_SOC_DAIFMT_CBM_CFS:
 145                pxa_i2s.master = 0;
 146                break;
 147        default:
 148                break;
 149        }
 150        return 0;
 151}
 152
 153static int pxa2xx_i2s_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
 154                int clk_id, unsigned int freq, int dir)
 155{
 156        if (clk_id != PXA2XX_I2S_SYSCLK)
 157                return -ENODEV;
 158
 159        return 0;
 160}
 161
 162static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream,
 163                                struct snd_pcm_hw_params *params,
 164                                struct snd_soc_dai *dai)
 165{
 166        struct snd_dmaengine_dai_dma_data *dma_data;
 167
 168        if (WARN_ON(IS_ERR(clk_i2s)))
 169                return -EINVAL;
 170        clk_prepare_enable(clk_i2s);
 171        clk_ena = 1;
 172        pxa_i2s_wait();
 173
 174        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 175                dma_data = &pxa2xx_i2s_pcm_stereo_out;
 176        else
 177                dma_data = &pxa2xx_i2s_pcm_stereo_in;
 178
 179        snd_soc_dai_set_dma_data(dai, substream, dma_data);
 180
 181        /* is port used by another stream */
 182        if (!(SACR0 & SACR0_ENB)) {
 183                SACR0 = 0;
 184                if (pxa_i2s.master)
 185                        SACR0 |= SACR0_BCKD;
 186
 187                SACR0 |= SACR0_RFTH(14) | SACR0_TFTH(1);
 188                SACR1 |= pxa_i2s.fmt;
 189        }
 190        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 191                SAIMR |= SAIMR_TFS;
 192        else
 193                SAIMR |= SAIMR_RFS;
 194
 195        switch (params_rate(params)) {
 196        case 8000:
 197                SADIV = 0x48;
 198                break;
 199        case 11025:
 200                SADIV = 0x34;
 201                break;
 202        case 16000:
 203                SADIV = 0x24;
 204                break;
 205        case 22050:
 206                SADIV = 0x1a;
 207                break;
 208        case 44100:
 209                SADIV = 0xd;
 210                break;
 211        case 48000:
 212                SADIV = 0xc;
 213                break;
 214        case 96000: /* not in manual and possibly slightly inaccurate */
 215                SADIV = 0x6;
 216                break;
 217        }
 218
 219        return 0;
 220}
 221
 222static int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
 223                              struct snd_soc_dai *dai)
 224{
 225        int ret = 0;
 226
 227        switch (cmd) {
 228        case SNDRV_PCM_TRIGGER_START:
 229                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 230                        SACR1 &= ~SACR1_DRPL;
 231                else
 232                        SACR1 &= ~SACR1_DREC;
 233                SACR0 |= SACR0_ENB;
 234                break;
 235        case SNDRV_PCM_TRIGGER_RESUME:
 236        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 237        case SNDRV_PCM_TRIGGER_STOP:
 238        case SNDRV_PCM_TRIGGER_SUSPEND:
 239        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 240                break;
 241        default:
 242                ret = -EINVAL;
 243        }
 244
 245        return ret;
 246}
 247
 248static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream,
 249                                struct snd_soc_dai *dai)
 250{
 251        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 252                SACR1 |= SACR1_DRPL;
 253                SAIMR &= ~SAIMR_TFS;
 254        } else {
 255                SACR1 |= SACR1_DREC;
 256                SAIMR &= ~SAIMR_RFS;
 257        }
 258
 259        if ((SACR1 & (SACR1_DREC | SACR1_DRPL)) == (SACR1_DREC | SACR1_DRPL)) {
 260                SACR0 &= ~SACR0_ENB;
 261                pxa_i2s_wait();
 262                if (clk_ena) {
 263                        clk_disable_unprepare(clk_i2s);
 264                        clk_ena = 0;
 265                }
 266        }
 267}
 268
 269#ifdef CONFIG_PM
 270static int pxa2xx_i2s_suspend(struct snd_soc_dai *dai)
 271{
 272        /* store registers */
 273        pxa_i2s.sacr0 = SACR0;
 274        pxa_i2s.sacr1 = SACR1;
 275        pxa_i2s.saimr = SAIMR;
 276        pxa_i2s.sadiv = SADIV;
 277
 278        /* deactivate link */
 279        SACR0 &= ~SACR0_ENB;
 280        pxa_i2s_wait();
 281        return 0;
 282}
 283
 284static int pxa2xx_i2s_resume(struct snd_soc_dai *dai)
 285{
 286        pxa_i2s_wait();
 287
 288        SACR0 = pxa_i2s.sacr0 & ~SACR0_ENB;
 289        SACR1 = pxa_i2s.sacr1;
 290        SAIMR = pxa_i2s.saimr;
 291        SADIV = pxa_i2s.sadiv;
 292
 293        SACR0 = pxa_i2s.sacr0;
 294
 295        return 0;
 296}
 297
 298#else
 299#define pxa2xx_i2s_suspend      NULL
 300#define pxa2xx_i2s_resume       NULL
 301#endif
 302
 303static int pxa2xx_i2s_probe(struct snd_soc_dai *dai)
 304{
 305        clk_i2s = clk_get(dai->dev, "I2SCLK");
 306        if (IS_ERR(clk_i2s))
 307                return PTR_ERR(clk_i2s);
 308
 309        /*
 310         * PXA Developer's Manual:
 311         * If SACR0[ENB] is toggled in the middle of a normal operation,
 312         * the SACR0[RST] bit must also be set and cleared to reset all
 313         * I2S controller registers.
 314         */
 315        SACR0 = SACR0_RST;
 316        SACR0 = 0;
 317        /* Make sure RPL and REC are disabled */
 318        SACR1 = SACR1_DRPL | SACR1_DREC;
 319        /* Along with FIFO servicing */
 320        SAIMR &= ~(SAIMR_RFS | SAIMR_TFS);
 321
 322        return 0;
 323}
 324
 325static int  pxa2xx_i2s_remove(struct snd_soc_dai *dai)
 326{
 327        clk_put(clk_i2s);
 328        clk_i2s = ERR_PTR(-ENOENT);
 329        return 0;
 330}
 331
 332#define PXA2XX_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
 333                SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
 334                SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
 335
 336static const struct snd_soc_dai_ops pxa_i2s_dai_ops = {
 337        .startup        = pxa2xx_i2s_startup,
 338        .shutdown       = pxa2xx_i2s_shutdown,
 339        .trigger        = pxa2xx_i2s_trigger,
 340        .hw_params      = pxa2xx_i2s_hw_params,
 341        .set_fmt        = pxa2xx_i2s_set_dai_fmt,
 342        .set_sysclk     = pxa2xx_i2s_set_dai_sysclk,
 343};
 344
 345static struct snd_soc_dai_driver pxa_i2s_dai = {
 346        .probe = pxa2xx_i2s_probe,
 347        .remove = pxa2xx_i2s_remove,
 348        .suspend = pxa2xx_i2s_suspend,
 349        .resume = pxa2xx_i2s_resume,
 350        .playback = {
 351                .channels_min = 2,
 352                .channels_max = 2,
 353                .rates = PXA2XX_I2S_RATES,
 354                .formats = SNDRV_PCM_FMTBIT_S16_LE,},
 355        .capture = {
 356                .channels_min = 2,
 357                .channels_max = 2,
 358                .rates = PXA2XX_I2S_RATES,
 359                .formats = SNDRV_PCM_FMTBIT_S16_LE,},
 360        .ops = &pxa_i2s_dai_ops,
 361        .symmetric_rates = 1,
 362};
 363
 364static const struct snd_soc_component_driver pxa_i2s_component = {
 365        .name           = "pxa-i2s",
 366};
 367
 368static int pxa2xx_i2s_drv_probe(struct platform_device *pdev)
 369{
 370        return snd_soc_register_component(&pdev->dev, &pxa_i2s_component,
 371                                          &pxa_i2s_dai, 1);
 372}
 373
 374static int pxa2xx_i2s_drv_remove(struct platform_device *pdev)
 375{
 376        snd_soc_unregister_component(&pdev->dev);
 377        return 0;
 378}
 379
 380static struct platform_driver pxa2xx_i2s_driver = {
 381        .probe = pxa2xx_i2s_drv_probe,
 382        .remove = pxa2xx_i2s_drv_remove,
 383
 384        .driver = {
 385                .name = "pxa2xx-i2s",
 386        },
 387};
 388
 389static int __init pxa2xx_i2s_init(void)
 390{
 391        clk_i2s = ERR_PTR(-ENOENT);
 392        return platform_driver_register(&pxa2xx_i2s_driver);
 393}
 394
 395static void __exit pxa2xx_i2s_exit(void)
 396{
 397        platform_driver_unregister(&pxa2xx_i2s_driver);
 398}
 399
 400module_init(pxa2xx_i2s_init);
 401module_exit(pxa2xx_i2s_exit);
 402
 403/* Module information */
 404MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk");
 405MODULE_DESCRIPTION("pxa2xx I2S SoC Interface");
 406MODULE_LICENSE("GPL");
 407MODULE_ALIAS("platform:pxa2xx-i2s");
 408