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 *         liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
   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 *  Revision history
  14 *    12th Aug 2005   Initial version.
  15 */
  16
  17#include <linux/init.h>
  18#include <linux/module.h>
  19#include <linux/device.h>
  20#include <linux/delay.h>
  21#include <sound/driver.h>
  22#include <sound/core.h>
  23#include <sound/pcm.h>
  24#include <sound/initval.h>
  25#include <sound/soc.h>
  26
  27#include <asm/hardware.h>
  28#include <asm/arch/pxa-regs.h>
  29#include <asm/arch/audio.h>
  30
  31#include "pxa2xx-pcm.h"
  32#include "pxa2xx-i2s.h"
  33
  34struct pxa_i2s_port {
  35        u32 sadiv;
  36        u32 sacr0;
  37        u32 sacr1;
  38        u32 saimr;
  39        int master;
  40        u32 fmt;
  41};
  42static struct pxa_i2s_port pxa_i2s;
  43
  44static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_out = {
  45        .name                   = "I2S PCM Stereo out",
  46        .dev_addr               = __PREG(SADR),
  47        .drcmr                  = &DRCMRTXSADR,
  48        .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
  49                                  DCMD_BURST32 | DCMD_WIDTH4,
  50};
  51
  52static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_in = {
  53        .name                   = "I2S PCM Stereo in",
  54        .dev_addr               = __PREG(SADR),
  55        .drcmr                  = &DRCMRRXSADR,
  56        .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
  57                                  DCMD_BURST32 | DCMD_WIDTH4,
  58};
  59
  60static struct pxa2xx_gpio gpio_bus[] = {
  61        { /* I2S SoC Slave */
  62                .rx = GPIO29_SDATA_IN_I2S_MD,
  63                .tx = GPIO30_SDATA_OUT_I2S_MD,
  64                .clk = GPIO28_BITCLK_IN_I2S_MD,
  65                .frm = GPIO31_SYNC_I2S_MD,
  66        },
  67        { /* I2S SoC Master */
  68#ifdef CONFIG_PXA27x
  69                .sys = GPIO113_I2S_SYSCLK_MD,
  70#else
  71                .sys = GPIO32_SYSCLK_I2S_MD,
  72#endif
  73                .rx = GPIO29_SDATA_IN_I2S_MD,
  74                .tx = GPIO30_SDATA_OUT_I2S_MD,
  75                .clk = GPIO28_BITCLK_OUT_I2S_MD,
  76                .frm = GPIO31_SYNC_I2S_MD,
  77        },
  78};
  79
  80static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream)
  81{
  82        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  83        struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
  84
  85        if (!cpu_dai->active) {
  86                SACR0 |= SACR0_RST;
  87                SACR0 = 0;
  88        }
  89
  90        return 0;
  91}
  92
  93/* wait for I2S controller to be ready */
  94static int pxa_i2s_wait(void)
  95{
  96        int i;
  97
  98        /* flush the Rx FIFO */
  99        for(i = 0; i < 16; i++)
 100                SADR;
 101        return 0;
 102}
 103
 104static int pxa2xx_i2s_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
 105                unsigned int fmt)
 106{
 107        /* interface format */
 108        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 109        case SND_SOC_DAIFMT_I2S:
 110                pxa_i2s.fmt = 0;
 111                break;
 112        case SND_SOC_DAIFMT_LEFT_J:
 113                pxa_i2s.fmt = SACR1_AMSL;
 114                break;
 115        }
 116
 117        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 118        case SND_SOC_DAIFMT_CBS_CFS:
 119                pxa_i2s.master = 1;
 120                break;
 121        case SND_SOC_DAIFMT_CBM_CFS:
 122                pxa_i2s.master = 0;
 123                break;
 124        default:
 125                break;
 126        }
 127        return 0;
 128}
 129
 130static int pxa2xx_i2s_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
 131                int clk_id, unsigned int freq, int dir)
 132{
 133        if (clk_id != PXA2XX_I2S_SYSCLK)
 134                return -ENODEV;
 135
 136        if (pxa_i2s.master && dir == SND_SOC_CLOCK_OUT)
 137                pxa_gpio_mode(gpio_bus[pxa_i2s.master].sys);
 138
 139        return 0;
 140}
 141
 142static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream,
 143                                struct snd_pcm_hw_params *params)
 144{
 145        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 146        struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
 147
 148        pxa_gpio_mode(gpio_bus[pxa_i2s.master].rx);
 149        pxa_gpio_mode(gpio_bus[pxa_i2s.master].tx);
 150        pxa_gpio_mode(gpio_bus[pxa_i2s.master].frm);
 151        pxa_gpio_mode(gpio_bus[pxa_i2s.master].clk);
 152        pxa_set_cken(CKEN_I2S, 1);
 153        pxa_i2s_wait();
 154
 155        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 156                cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_out;
 157        else
 158                cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_in;
 159
 160        /* is port used by another stream */
 161        if (!(SACR0 & SACR0_ENB)) {
 162
 163                SACR0 = 0;
 164                SACR1 = 0;
 165                if (pxa_i2s.master)
 166                        SACR0 |= SACR0_BCKD;
 167
 168                SACR0 |= SACR0_RFTH(14) | SACR0_TFTH(1);
 169                SACR1 |= pxa_i2s.fmt;
 170        }
 171        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 172                SAIMR |= SAIMR_TFS;
 173        else
 174                SAIMR |= SAIMR_RFS;
 175
 176        switch (params_rate(params)) {
 177        case 8000:
 178                SADIV = 0x48;
 179                break;
 180        case 11025:
 181                SADIV = 0x34;
 182                break;
 183        case 16000:
 184                SADIV = 0x24;
 185                break;
 186        case 22050:
 187                SADIV = 0x1a;
 188                break;
 189        case 44100:
 190                SADIV = 0xd;
 191                break;
 192        case 48000:
 193                SADIV = 0xc;
 194                break;
 195        case 96000: /* not in manual and possibly slightly inaccurate */
 196                SADIV = 0x6;
 197                break;
 198        }
 199
 200        return 0;
 201}
 202
 203static int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
 204{
 205        int ret = 0;
 206
 207        switch (cmd) {
 208        case SNDRV_PCM_TRIGGER_START:
 209                SACR0 |= SACR0_ENB;
 210                break;
 211        case SNDRV_PCM_TRIGGER_RESUME:
 212        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 213        case SNDRV_PCM_TRIGGER_STOP:
 214        case SNDRV_PCM_TRIGGER_SUSPEND:
 215        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 216                break;
 217        default:
 218                ret = -EINVAL;
 219        }
 220
 221        return ret;
 222}
 223
 224static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream)
 225{
 226        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 227                SACR1 |= SACR1_DRPL;
 228                SAIMR &= ~SAIMR_TFS;
 229        } else {
 230                SACR1 |= SACR1_DREC;
 231                SAIMR &= ~SAIMR_RFS;
 232        }
 233
 234        if (SACR1 & (SACR1_DREC | SACR1_DRPL)) {
 235                SACR0 &= ~SACR0_ENB;
 236                pxa_i2s_wait();
 237                pxa_set_cken(CKEN_I2S, 0);
 238        }
 239}
 240
 241#ifdef CONFIG_PM
 242static int pxa2xx_i2s_suspend(struct platform_device *dev,
 243        struct snd_soc_cpu_dai *dai)
 244{
 245        if (!dai->active)
 246                return 0;
 247
 248        /* store registers */
 249        pxa_i2s.sacr0 = SACR0;
 250        pxa_i2s.sacr1 = SACR1;
 251        pxa_i2s.saimr = SAIMR;
 252        pxa_i2s.sadiv = SADIV;
 253
 254        /* deactivate link */
 255        SACR0 &= ~SACR0_ENB;
 256        pxa_i2s_wait();
 257        return 0;
 258}
 259
 260static int pxa2xx_i2s_resume(struct platform_device *pdev,
 261        struct snd_soc_cpu_dai *dai)
 262{
 263        if (!dai->active)
 264                return 0;
 265
 266        pxa_i2s_wait();
 267
 268        SACR0 = pxa_i2s.sacr0 &= ~SACR0_ENB;
 269        SACR1 = pxa_i2s.sacr1;
 270        SAIMR = pxa_i2s.saimr;
 271        SADIV = pxa_i2s.sadiv;
 272        SACR0 |= SACR0_ENB;
 273
 274        return 0;
 275}
 276
 277#else
 278#define pxa2xx_i2s_suspend      NULL
 279#define pxa2xx_i2s_resume       NULL
 280#endif
 281
 282#define PXA2XX_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
 283                SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
 284                SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
 285
 286struct snd_soc_cpu_dai pxa_i2s_dai = {
 287        .name = "pxa2xx-i2s",
 288        .id = 0,
 289        .type = SND_SOC_DAI_I2S,
 290        .suspend = pxa2xx_i2s_suspend,
 291        .resume = pxa2xx_i2s_resume,
 292        .playback = {
 293                .channels_min = 2,
 294                .channels_max = 2,
 295                .rates = PXA2XX_I2S_RATES,
 296                .formats = SNDRV_PCM_FMTBIT_S16_LE,},
 297        .capture = {
 298                .channels_min = 2,
 299                .channels_max = 2,
 300                .rates = PXA2XX_I2S_RATES,
 301                .formats = SNDRV_PCM_FMTBIT_S16_LE,},
 302        .ops = {
 303                .startup = pxa2xx_i2s_startup,
 304                .shutdown = pxa2xx_i2s_shutdown,
 305                .trigger = pxa2xx_i2s_trigger,
 306                .hw_params = pxa2xx_i2s_hw_params,},
 307        .dai_ops = {
 308                .set_fmt = pxa2xx_i2s_set_dai_fmt,
 309                .set_sysclk = pxa2xx_i2s_set_dai_sysclk,
 310        },
 311};
 312
 313EXPORT_SYMBOL_GPL(pxa_i2s_dai);
 314
 315/* Module information */
 316MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
 317MODULE_DESCRIPTION("pxa2xx I2S SoC Interface");
 318MODULE_LICENSE("GPL");
 319