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 ssi_priv *ssi = &ssi_cpu_data[dai->id];
  96        if (ssi->inuse) {
  97                pr_debug("ssi: already in use!\n");
  98                return -EBUSY;
  99        } else
 100                ssi->inuse = 1;
 101        return 0;
 102}
 103
 104static void ssi_shutdown(struct snd_pcm_substream *substream,
 105                         struct snd_soc_dai *dai)
 106{
 107        struct ssi_priv *ssi = &ssi_cpu_data[dai->id];
 108
 109        ssi->inuse = 0;
 110}
 111
 112static int ssi_trigger(struct snd_pcm_substream *substream, int cmd,
 113                       struct snd_soc_dai *dai)
 114{
 115        struct ssi_priv *ssi = &ssi_cpu_data[dai->id];
 116
 117        switch (cmd) {
 118        case SNDRV_PCM_TRIGGER_START:
 119                SSIREG(SSICR) |= CR_DMAEN | CR_EN;
 120                break;
 121        case SNDRV_PCM_TRIGGER_STOP:
 122                SSIREG(SSICR) &= ~(CR_DMAEN | CR_EN);
 123                break;
 124        default:
 125                return -EINVAL;
 126        }
 127
 128        return 0;
 129}
 130
 131static int ssi_hw_params(struct snd_pcm_substream *substream,
 132                         struct snd_pcm_hw_params *params,
 133                         struct snd_soc_dai *dai)
 134{
 135        struct ssi_priv *ssi = &ssi_cpu_data[dai->id];
 136        unsigned long ssicr = SSIREG(SSICR);
 137        unsigned int bits, channels, swl, recv, i;
 138
 139        channels = params_channels(params);
 140        bits = params->msbits;
 141        recv = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 0 : 1;
 142
 143        pr_debug("ssi_hw_params() enter\nssicr was    %08lx\n", ssicr);
 144        pr_debug("bits: %u channels: %u\n", bits, channels);
 145
 146        ssicr &= ~(CR_TRMD | CR_CHNL_MASK | CR_DWL_MASK | CR_PDTA |
 147                   CR_SWL_MASK);
 148
 149        /* direction (send/receive) */
 150        if (!recv)
 151                ssicr |= CR_TRMD;       /* transmit */
 152
 153        /* channels */
 154        if ((channels < 2) || (channels > 8) || (channels & 1)) {
 155                pr_debug("ssi: invalid number of channels\n");
 156                return -EINVAL;
 157        }
 158        ssicr |= ((channels >> 1) - 1) << CR_CHNL_SHIFT;
 159
 160        /* DATA WORD LENGTH (DWL): databits in audio sample */
 161        i = 0;
 162        switch (bits) {
 163        case 32: ++i;
 164        case 24: ++i;
 165        case 22: ++i;
 166        case 20: ++i;
 167        case 18: ++i;
 168        case 16: ++i;
 169                 ssicr |= i << CR_DWL_SHIFT;
 170        case 8:  break;
 171        default:
 172                pr_debug("ssi: invalid sample width\n");
 173                return -EINVAL;
 174        }
 175
 176        /*
 177         * SYSTEM WORD LENGTH: size in bits of half a frame over the I2S
 178         * wires. This is usually bits_per_sample x channels/2;  i.e. in
 179         * Stereo mode  the SWL equals DWL.  SWL can  be bigger than the
 180         * product of (channels_per_slot x samplebits), e.g.  for codecs
 181         * like the AD1939 which  only accept 32bit wide TDM slots.  For
 182         * "standard" I2S operation we set SWL = chans / 2 * DWL here.
 183         * Waiting for ASoC to get TDM support ;-)
 184         */
 185        if ((bits > 16) && (bits <= 24)) {
 186                bits = 24;      /* these are padded by the SSI */
 187                /*ssicr |= CR_PDTA;*/ /* cpu/data endianness ? */
 188        }
 189        i = 0;
 190        swl = (bits * channels) / 2;
 191        switch (swl) {
 192        case 256: ++i;
 193        case 128: ++i;
 194        case 64:  ++i;
 195        case 48:  ++i;
 196        case 32:  ++i;
 197        case 16:  ++i;
 198                  ssicr |= i << CR_SWL_SHIFT;
 199        case 8:   break;
 200        default:
 201                pr_debug("ssi: invalid system word length computed\n");
 202                return -EINVAL;
 203        }
 204
 205        SSIREG(SSICR) = ssicr;
 206
 207        pr_debug("ssi_hw_params() leave\nssicr is now %08lx\n", ssicr);
 208        return 0;
 209}
 210
 211static int ssi_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id,
 212                          unsigned int freq, int dir)
 213{
 214        struct ssi_priv *ssi = &ssi_cpu_data[cpu_dai->id];
 215
 216        ssi->sysclk = freq;
 217
 218        return 0;
 219}
 220
 221/*
 222 * This divider is used to generate the SSI_SCK (I2S bitclock) from the
 223 * clock at the HAC_BIT_CLK ("oversampling clock") pin.
 224 */
 225static int ssi_set_clkdiv(struct snd_soc_dai *dai, int did, int div)
 226{
 227        struct ssi_priv *ssi = &ssi_cpu_data[dai->id];
 228        unsigned long ssicr;
 229        int i;
 230
 231        i = 0;
 232        ssicr = SSIREG(SSICR) & ~CR_CKDIV_MASK;
 233        switch (div) {
 234        case 16: ++i;
 235        case 8:  ++i;
 236        case 4:  ++i;
 237        case 2:  ++i;
 238                 SSIREG(SSICR) = ssicr | (i << CR_CKDIV_SHIFT);
 239        case 1:  break;
 240        default:
 241                pr_debug("ssi: invalid sck divider %d\n", div);
 242                return -EINVAL;
 243        }
 244
 245        return 0;
 246}
 247
 248static int ssi_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 249{
 250        struct ssi_priv *ssi = &ssi_cpu_data[dai->id];
 251        unsigned long ssicr = SSIREG(SSICR);
 252
 253        pr_debug("ssi_set_fmt()\nssicr was    0x%08lx\n", ssicr);
 254
 255        ssicr &= ~(CR_DEL | CR_PDTA | CR_BREN | CR_SWSP | CR_SCKP |
 256                   CR_SWS_MASTER | CR_SCK_MASTER);
 257
 258        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 259        case SND_SOC_DAIFMT_I2S:
 260                break;
 261        case SND_SOC_DAIFMT_RIGHT_J:
 262                ssicr |= CR_DEL | CR_PDTA;
 263                break;
 264        case SND_SOC_DAIFMT_LEFT_J:
 265                ssicr |= CR_DEL;
 266                break;
 267        default:
 268                pr_debug("ssi: unsupported format\n");
 269                return -EINVAL;
 270        }
 271
 272        switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
 273        case SND_SOC_DAIFMT_CONT:
 274                break;
 275        case SND_SOC_DAIFMT_GATED:
 276                ssicr |= CR_BREN;
 277                break;
 278        }
 279
 280        switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
 281        case SND_SOC_DAIFMT_NB_NF:
 282                ssicr |= CR_SCKP;       /* sample data at low clkedge */
 283                break;
 284        case SND_SOC_DAIFMT_NB_IF:
 285                ssicr |= CR_SCKP | CR_SWSP;
 286                break;
 287        case SND_SOC_DAIFMT_IB_NF:
 288                break;
 289        case SND_SOC_DAIFMT_IB_IF:
 290                ssicr |= CR_SWSP;       /* word select starts low */
 291                break;
 292        default:
 293                pr_debug("ssi: invalid inversion\n");
 294                return -EINVAL;
 295        }
 296
 297        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 298        case SND_SOC_DAIFMT_CBM_CFM:
 299                break;
 300        case SND_SOC_DAIFMT_CBS_CFM:
 301                ssicr |= CR_SCK_MASTER;
 302                break;
 303        case SND_SOC_DAIFMT_CBM_CFS:
 304                ssicr |= CR_SWS_MASTER;
 305                break;
 306        case SND_SOC_DAIFMT_CBS_CFS:
 307                ssicr |= CR_SWS_MASTER | CR_SCK_MASTER;
 308                break;
 309        default:
 310                pr_debug("ssi: invalid master/slave configuration\n");
 311                return -EINVAL;
 312        }
 313
 314        SSIREG(SSICR) = ssicr;
 315        pr_debug("ssi_set_fmt() leave\nssicr is now 0x%08lx\n", ssicr);
 316
 317        return 0;
 318}
 319
 320/* the SSI depends on an external clocksource (at HAC_BIT_CLK) even in
 321 * Master mode,  so really this is board specific;  the SSI can do any
 322 * rate with the right bitclk and divider settings.
 323 */
 324#define SSI_RATES       \
 325        SNDRV_PCM_RATE_8000_192000
 326
 327/* the SSI can do 8-32 bit samples, with 8 possible channels */
 328#define SSI_FMTS        \
 329        (SNDRV_PCM_FMTBIT_S8      | SNDRV_PCM_FMTBIT_U8      |  \
 330         SNDRV_PCM_FMTBIT_S16_LE  | SNDRV_PCM_FMTBIT_U16_LE  |  \
 331         SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE |  \
 332         SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3LE |  \
 333         SNDRV_PCM_FMTBIT_S32_LE  | SNDRV_PCM_FMTBIT_U32_LE)
 334
 335static const struct snd_soc_dai_ops ssi_dai_ops = {
 336        .startup        = ssi_startup,
 337        .shutdown       = ssi_shutdown,
 338        .trigger        = ssi_trigger,
 339        .hw_params      = ssi_hw_params,
 340        .set_sysclk     = ssi_set_sysclk,
 341        .set_clkdiv     = ssi_set_clkdiv,
 342        .set_fmt        = ssi_set_fmt,
 343};
 344
 345static struct snd_soc_dai_driver sh4_ssi_dai[] = {
 346{
 347        .name                   = "ssi-dai.0",
 348        .playback = {
 349                .rates          = SSI_RATES,
 350                .formats        = SSI_FMTS,
 351                .channels_min   = 2,
 352                .channels_max   = 8,
 353        },
 354        .capture = {
 355                .rates          = SSI_RATES,
 356                .formats        = SSI_FMTS,
 357                .channels_min   = 2,
 358                .channels_max   = 8,
 359        },
 360        .ops = &ssi_dai_ops,
 361},
 362#ifdef CONFIG_CPU_SUBTYPE_SH7760
 363{
 364        .name                   = "ssi-dai.1",
 365        .playback = {
 366                .rates          = SSI_RATES,
 367                .formats        = SSI_FMTS,
 368                .channels_min   = 2,
 369                .channels_max   = 8,
 370        },
 371        .capture = {
 372                .rates          = SSI_RATES,
 373                .formats        = SSI_FMTS,
 374                .channels_min   = 2,
 375                .channels_max   = 8,
 376        },
 377        .ops = &ssi_dai_ops,
 378},
 379#endif
 380};
 381
 382static const struct snd_soc_component_driver sh4_ssi_component = {
 383        .name           = "sh4-ssi",
 384};
 385
 386static int sh4_soc_dai_probe(struct platform_device *pdev)
 387{
 388        return devm_snd_soc_register_component(&pdev->dev, &sh4_ssi_component,
 389                                               sh4_ssi_dai,
 390                                               ARRAY_SIZE(sh4_ssi_dai));
 391}
 392
 393static struct platform_driver sh4_ssi_driver = {
 394        .driver = {
 395                        .name = "sh4-ssi-dai",
 396        },
 397
 398        .probe = sh4_soc_dai_probe,
 399};
 400
 401module_platform_driver(sh4_ssi_driver);
 402
 403MODULE_LICENSE("GPL");
 404MODULE_DESCRIPTION("SuperH onchip SSI (I2S) audio driver");
 405MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
 406