linux/sound/soc/sh/ssi.c
<<
>>
Prefs
   1/*
   2 * Serial Sound Interface (I2S) support for SH7760/SH7780
   3 *
   4 * Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net>
   5 *
   6 *  licensed under the terms outlined in the file COPYING at the root
   7 *  of the linux kernel sources.
   8 *
   9 * dont forget to set IPSEL/OMSEL register bits (in your board code) to
  10 * enable SSI output pins!
  11 */
  12
  13/*
  14 * LIMITATIONS:
  15 *      The SSI unit has only one physical data line, so full duplex is
  16 *      impossible.  This can be remedied  on the  SH7760 by  using the
  17 *      other SSI unit for recording; however the SH7780 has only 1 SSI
  18 *      unit, and its pins are shared with the AC97 unit,  among others.
  19 *
  20 * FEATURES:
  21 *      The SSI features "compressed mode": in this mode it continuously
  22 *      streams PCM data over the I2S lines and uses LRCK as a handshake
  23 *      signal.  Can be used to send compressed data (AC3/DTS) to a DSP.
  24 *      The number of bits sent over the wire in a frame can be adjusted
  25 *      and can be independent from the actual sample bit depth. This is
  26 *      useful to support TDM mode codecs like the AD1939 which have a
  27 *      fixed TDM slot size, regardless of sample resolution.
  28 */
  29
  30#include <linux/init.h>
  31#include <linux/module.h>
  32#include <linux/platform_device.h>
  33#include <sound/core.h>
  34#include <sound/pcm.h>
  35#include <sound/initval.h>
  36#include <sound/soc.h>
  37#include <asm/io.h>
  38
  39#define SSICR   0x00
  40#define SSISR   0x04
  41
  42#define CR_DMAEN        (1 << 28)
  43#define CR_CHNL_SHIFT   22
  44#define CR_CHNL_MASK    (3 << CR_CHNL_SHIFT)
  45#define CR_DWL_SHIFT    19
  46#define CR_DWL_MASK     (7 << CR_DWL_SHIFT)
  47#define CR_SWL_SHIFT    16
  48#define CR_SWL_MASK     (7 << CR_SWL_SHIFT)
  49#define CR_SCK_MASTER   (1 << 15)       /* bitclock master bit */
  50#define CR_SWS_MASTER   (1 << 14)       /* wordselect master bit */
  51#define CR_SCKP         (1 << 13)       /* I2Sclock polarity */
  52#define CR_SWSP         (1 << 12)       /* LRCK polarity */
  53#define CR_SPDP         (1 << 11)
  54#define CR_SDTA         (1 << 10)       /* i2s alignment (msb/lsb) */
  55#define CR_PDTA         (1 << 9)        /* fifo data alignment */
  56#define CR_DEL          (1 << 8)        /* delay data by 1 i2sclk */
  57#define CR_BREN         (1 << 7)        /* clock gating in burst mode */
  58#define CR_CKDIV_SHIFT  4
  59#define CR_CKDIV_MASK   (7 << CR_CKDIV_SHIFT)   /* bitclock divider */
  60#define CR_MUTE         (1 << 3)        /* SSI mute */
  61#define CR_CPEN         (1 << 2)        /* compressed mode */
  62#define CR_TRMD         (1 << 1)        /* transmit/receive select */
  63#define CR_EN           (1 << 0)        /* enable SSI */
  64
  65#define SSIREG(reg)     (*(unsigned long *)(ssi->mmio + (reg)))
  66
  67struct ssi_priv {
  68        unsigned long mmio;
  69        unsigned long sysclk;
  70        int inuse;
  71} ssi_cpu_data[] = {
  72#if defined(CONFIG_CPU_SUBTYPE_SH7760)
  73        {
  74                .mmio   = 0xFE680000,
  75        },
  76        {
  77                .mmio   = 0xFE690000,
  78        },
  79#elif defined(CONFIG_CPU_SUBTYPE_SH7780)
  80        {
  81                .mmio   = 0xFFE70000,
  82        },
  83#else
  84#error "Unsupported SuperH SoC"
  85#endif
  86};
  87
  88/*
  89 * track usage of the SSI; it is simplex-only so prevent attempts of
  90 * concurrent playback + capture. FIXME: any locking required?
  91 */
  92static int ssi_startup(struct snd_pcm_substream *substream,
  93                       struct snd_soc_dai *dai)
  94{
  95        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  96        struct ssi_priv *ssi = &ssi_cpu_data[rtd->dai->cpu_dai->id];
  97        if (ssi->inuse) {
  98                pr_debug("ssi: already in use!\n");
  99                return -EBUSY;
 100        } else
 101                ssi->inuse = 1;
 102        return 0;
 103}
 104
 105static void ssi_shutdown(struct snd_pcm_substream *substream,
 106                         struct snd_soc_dai *dai)
 107{
 108        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 109        struct ssi_priv *ssi = &ssi_cpu_data[rtd->dai->cpu_dai->id];
 110
 111        ssi->inuse = 0;
 112}
 113
 114static int ssi_trigger(struct snd_pcm_substream *substream, int cmd,
 115                       struct snd_soc_dai *dai)
 116{
 117        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 118        struct ssi_priv *ssi = &ssi_cpu_data[rtd->dai->cpu_dai->id];
 119
 120        switch (cmd) {
 121        case SNDRV_PCM_TRIGGER_START:
 122                SSIREG(SSICR) |= CR_DMAEN | CR_EN;
 123                break;
 124        case SNDRV_PCM_TRIGGER_STOP:
 125                SSIREG(SSICR) &= ~(CR_DMAEN | CR_EN);
 126                break;
 127        default:
 128                return -EINVAL;
 129        }
 130
 131        return 0;
 132}
 133
 134static int ssi_hw_params(struct snd_pcm_substream *substream,
 135                         struct snd_pcm_hw_params *params,
 136                         struct snd_soc_dai *dai)
 137{
 138        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 139        struct ssi_priv *ssi = &ssi_cpu_data[rtd->dai->cpu_dai->id];
 140        unsigned long ssicr = SSIREG(SSICR);
 141        unsigned int bits, channels, swl, recv, i;
 142
 143        channels = params_channels(params);
 144        bits = params->msbits;
 145        recv = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 0 : 1;
 146
 147        pr_debug("ssi_hw_params() enter\nssicr was    %08lx\n", ssicr);
 148        pr_debug("bits: %u channels: %u\n", bits, channels);
 149
 150        ssicr &= ~(CR_TRMD | CR_CHNL_MASK | CR_DWL_MASK | CR_PDTA |
 151                   CR_SWL_MASK);
 152
 153        /* direction (send/receive) */
 154        if (!recv)
 155                ssicr |= CR_TRMD;       /* transmit */
 156
 157        /* channels */
 158        if ((channels < 2) || (channels > 8) || (channels & 1)) {
 159                pr_debug("ssi: invalid number of channels\n");
 160                return -EINVAL;
 161        }
 162        ssicr |= ((channels >> 1) - 1) << CR_CHNL_SHIFT;
 163
 164        /* DATA WORD LENGTH (DWL): databits in audio sample */
 165        i = 0;
 166        switch (bits) {
 167        case 32: ++i;
 168        case 24: ++i;
 169        case 22: ++i;
 170        case 20: ++i;
 171        case 18: ++i;
 172        case 16: ++i;
 173                 ssicr |= i << CR_DWL_SHIFT;
 174        case 8:  break;
 175        default:
 176                pr_debug("ssi: invalid sample width\n");
 177                return -EINVAL;
 178        }
 179
 180        /*
 181         * SYSTEM WORD LENGTH: size in bits of half a frame over the I2S
 182         * wires. This is usually bits_per_sample x channels/2;  i.e. in
 183         * Stereo mode  the SWL equals DWL.  SWL can  be bigger than the
 184         * product of (channels_per_slot x samplebits), e.g.  for codecs
 185         * like the AD1939 which  only accept 32bit wide TDM slots.  For
 186         * "standard" I2S operation we set SWL = chans / 2 * DWL here.
 187         * Waiting for ASoC to get TDM support ;-)
 188         */
 189        if ((bits > 16) && (bits <= 24)) {
 190                bits = 24;      /* these are padded by the SSI */
 191                /*ssicr |= CR_PDTA;*/ /* cpu/data endianness ? */
 192        }
 193        i = 0;
 194        swl = (bits * channels) / 2;
 195        switch (swl) {
 196        case 256: ++i;
 197        case 128: ++i;
 198        case 64:  ++i;
 199        case 48:  ++i;
 200        case 32:  ++i;
 201        case 16:  ++i;
 202                  ssicr |= i << CR_SWL_SHIFT;
 203        case 8:   break;
 204        default:
 205                pr_debug("ssi: invalid system word length computed\n");
 206                return -EINVAL;
 207        }
 208
 209        SSIREG(SSICR) = ssicr;
 210
 211        pr_debug("ssi_hw_params() leave\nssicr is now %08lx\n", ssicr);
 212        return 0;
 213}
 214
 215static int ssi_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id,
 216                          unsigned int freq, int dir)
 217{
 218        struct ssi_priv *ssi = &ssi_cpu_data[cpu_dai->id];
 219
 220        ssi->sysclk = freq;
 221
 222        return 0;
 223}
 224
 225/*
 226 * This divider is used to generate the SSI_SCK (I2S bitclock) from the
 227 * clock at the HAC_BIT_CLK ("oversampling clock") pin.
 228 */
 229static int ssi_set_clkdiv(struct snd_soc_dai *dai, int did, int div)
 230{
 231        struct ssi_priv *ssi = &ssi_cpu_data[dai->id];
 232        unsigned long ssicr;
 233        int i;
 234
 235        i = 0;
 236        ssicr = SSIREG(SSICR) & ~CR_CKDIV_MASK;
 237        switch (div) {
 238        case 16: ++i;
 239        case 8:  ++i;
 240        case 4:  ++i;
 241        case 2:  ++i;
 242                 SSIREG(SSICR) = ssicr | (i << CR_CKDIV_SHIFT);
 243        case 1:  break;
 244        default:
 245                pr_debug("ssi: invalid sck divider %d\n", div);
 246                return -EINVAL;
 247        }
 248
 249        return 0;
 250}
 251
 252static int ssi_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 253{
 254        struct ssi_priv *ssi = &ssi_cpu_data[dai->id];
 255        unsigned long ssicr = SSIREG(SSICR);
 256
 257        pr_debug("ssi_set_fmt()\nssicr was    0x%08lx\n", ssicr);
 258
 259        ssicr &= ~(CR_DEL | CR_PDTA | CR_BREN | CR_SWSP | CR_SCKP |
 260                   CR_SWS_MASTER | CR_SCK_MASTER);
 261
 262        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 263        case SND_SOC_DAIFMT_I2S:
 264                break;
 265        case SND_SOC_DAIFMT_RIGHT_J:
 266                ssicr |= CR_DEL | CR_PDTA;
 267                break;
 268        case SND_SOC_DAIFMT_LEFT_J:
 269                ssicr |= CR_DEL;
 270                break;
 271        default:
 272                pr_debug("ssi: unsupported format\n");
 273                return -EINVAL;
 274        }
 275
 276        switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
 277        case SND_SOC_DAIFMT_CONT:
 278                break;
 279        case SND_SOC_DAIFMT_GATED:
 280                ssicr |= CR_BREN;
 281                break;
 282        }
 283
 284        switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
 285        case SND_SOC_DAIFMT_NB_NF:
 286                ssicr |= CR_SCKP;       /* sample data at low clkedge */
 287                break;
 288        case SND_SOC_DAIFMT_NB_IF:
 289                ssicr |= CR_SCKP | CR_SWSP;
 290                break;
 291        case SND_SOC_DAIFMT_IB_NF:
 292                break;
 293        case SND_SOC_DAIFMT_IB_IF:
 294                ssicr |= CR_SWSP;       /* word select starts low */
 295                break;
 296        default:
 297                pr_debug("ssi: invalid inversion\n");
 298                return -EINVAL;
 299        }
 300
 301        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 302        case SND_SOC_DAIFMT_CBM_CFM:
 303                break;
 304        case SND_SOC_DAIFMT_CBS_CFM:
 305                ssicr |= CR_SCK_MASTER;
 306                break;
 307        case SND_SOC_DAIFMT_CBM_CFS:
 308                ssicr |= CR_SWS_MASTER;
 309                break;
 310        case SND_SOC_DAIFMT_CBS_CFS:
 311                ssicr |= CR_SWS_MASTER | CR_SCK_MASTER;
 312                break;
 313        default:
 314                pr_debug("ssi: invalid master/slave configuration\n");
 315                return -EINVAL;
 316        }
 317
 318        SSIREG(SSICR) = ssicr;
 319        pr_debug("ssi_set_fmt() leave\nssicr is now 0x%08lx\n", ssicr);
 320
 321        return 0;
 322}
 323
 324/* the SSI depends on an external clocksource (at HAC_BIT_CLK) even in
 325 * Master mode,  so really this is board specific;  the SSI can do any
 326 * rate with the right bitclk and divider settings.
 327 */
 328#define SSI_RATES       \
 329        SNDRV_PCM_RATE_8000_192000
 330
 331/* the SSI can do 8-32 bit samples, with 8 possible channels */
 332#define SSI_FMTS        \
 333        (SNDRV_PCM_FMTBIT_S8      | SNDRV_PCM_FMTBIT_U8      |  \
 334         SNDRV_PCM_FMTBIT_S16_LE  | SNDRV_PCM_FMTBIT_U16_LE  |  \
 335         SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE |  \
 336         SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3LE |  \
 337         SNDRV_PCM_FMTBIT_S32_LE  | SNDRV_PCM_FMTBIT_U32_LE)
 338
 339static struct snd_soc_dai_ops ssi_dai_ops = {
 340        .startup        = ssi_startup,
 341        .shutdown       = ssi_shutdown,
 342        .trigger        = ssi_trigger,
 343        .hw_params      = ssi_hw_params,
 344        .set_sysclk     = ssi_set_sysclk,
 345        .set_clkdiv     = ssi_set_clkdiv,
 346        .set_fmt        = ssi_set_fmt,
 347};
 348
 349struct snd_soc_dai sh4_ssi_dai[] = {
 350{
 351        .name                   = "SSI0",
 352        .id                     = 0,
 353        .playback = {
 354                .rates          = SSI_RATES,
 355                .formats        = SSI_FMTS,
 356                .channels_min   = 2,
 357                .channels_max   = 8,
 358        },
 359        .capture = {
 360                .rates          = SSI_RATES,
 361                .formats        = SSI_FMTS,
 362                .channels_min   = 2,
 363                .channels_max   = 8,
 364        },
 365        .ops = &ssi_dai_ops,
 366},
 367#ifdef CONFIG_CPU_SUBTYPE_SH7760
 368{
 369        .name                   = "SSI1",
 370        .id                     = 1,
 371        .playback = {
 372                .rates          = SSI_RATES,
 373                .formats        = SSI_FMTS,
 374                .channels_min   = 2,
 375                .channels_max   = 8,
 376        },
 377        .capture = {
 378                .rates          = SSI_RATES,
 379                .formats        = SSI_FMTS,
 380                .channels_min   = 2,
 381                .channels_max   = 8,
 382        },
 383        .ops = &ssi_dai_ops,
 384},
 385#endif
 386};
 387EXPORT_SYMBOL_GPL(sh4_ssi_dai);
 388
 389static int __init sh4_ssi_init(void)
 390{
 391        return snd_soc_register_dais(sh4_ssi_dai, ARRAY_SIZE(sh4_ssi_dai));
 392}
 393module_init(sh4_ssi_init);
 394
 395static void __exit sh4_ssi_exit(void)
 396{
 397        snd_soc_unregister_dais(sh4_ssi_dai, ARRAY_SIZE(sh4_ssi_dai));
 398}
 399module_exit(sh4_ssi_exit);
 400
 401MODULE_LICENSE("GPL");
 402MODULE_DESCRIPTION("SuperH onchip SSI (I2S) audio driver");
 403MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
 404