linux/sound/soc/au1x/psc-i2s.c
<<
>>
Prefs
   1/*
   2 * Au12x0/Au1550 PSC ALSA ASoC audio support.
   3 *
   4 * (c) 2007-2008 MSC Vertriebsges.m.b.H.,
   5 *      Manuel Lauss <mano@roarinelk.homelinux.net>
   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 * Au1xxx-PSC I2S glue.
  12 *
  13 * NOTE: all of these drivers can only work with a SINGLE instance
  14 *       of a PSC. Multiple independent audio devices are impossible
  15 *       with ASoC v1.
  16 * NOTE: so far only PSC slave mode (bit- and frameclock) is supported.
  17 */
  18
  19#include <linux/init.h>
  20#include <linux/module.h>
  21#include <linux/suspend.h>
  22#include <sound/core.h>
  23#include <sound/pcm.h>
  24#include <sound/initval.h>
  25#include <sound/soc.h>
  26#include <asm/mach-au1x00/au1000.h>
  27#include <asm/mach-au1x00/au1xxx_psc.h>
  28
  29#include "psc.h"
  30
  31/* supported I2S DAI hardware formats */
  32#define AU1XPSC_I2S_DAIFMT \
  33        (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J |   \
  34         SND_SOC_DAIFMT_NB_NF)
  35
  36/* supported I2S direction */
  37#define AU1XPSC_I2S_DIR \
  38        (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
  39
  40#define AU1XPSC_I2S_RATES \
  41        SNDRV_PCM_RATE_8000_192000
  42
  43#define AU1XPSC_I2S_FMTS \
  44        (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
  45
  46#define I2SSTAT_BUSY(stype)     \
  47        ((stype) == PCM_TX ? PSC_I2SSTAT_TB : PSC_I2SSTAT_RB)
  48#define I2SPCR_START(stype)     \
  49        ((stype) == PCM_TX ? PSC_I2SPCR_TS : PSC_I2SPCR_RS)
  50#define I2SPCR_STOP(stype)      \
  51        ((stype) == PCM_TX ? PSC_I2SPCR_TP : PSC_I2SPCR_RP)
  52#define I2SPCR_CLRFIFO(stype)   \
  53        ((stype) == PCM_TX ? PSC_I2SPCR_TC : PSC_I2SPCR_RC)
  54
  55
  56/* instance data. There can be only one, MacLeod!!!! */
  57static struct au1xpsc_audio_data *au1xpsc_i2s_workdata;
  58
  59static int au1xpsc_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
  60                               unsigned int fmt)
  61{
  62        struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
  63        unsigned long ct;
  64        int ret;
  65
  66        ret = -EINVAL;
  67
  68        ct = pscdata->cfg;
  69
  70        ct &= ~(PSC_I2SCFG_XM | PSC_I2SCFG_MLJ);        /* left-justified */
  71        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
  72        case SND_SOC_DAIFMT_I2S:
  73                ct |= PSC_I2SCFG_XM;    /* enable I2S mode */
  74                break;
  75        case SND_SOC_DAIFMT_MSB:
  76                break;
  77        case SND_SOC_DAIFMT_LSB:
  78                ct |= PSC_I2SCFG_MLJ;   /* LSB (right-) justified */
  79                break;
  80        default:
  81                goto out;
  82        }
  83
  84        ct &= ~(PSC_I2SCFG_BI | PSC_I2SCFG_WI);         /* IB-IF */
  85        switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
  86        case SND_SOC_DAIFMT_NB_NF:
  87                ct |= PSC_I2SCFG_BI | PSC_I2SCFG_WI;
  88                break;
  89        case SND_SOC_DAIFMT_NB_IF:
  90                ct |= PSC_I2SCFG_BI;
  91                break;
  92        case SND_SOC_DAIFMT_IB_NF:
  93                ct |= PSC_I2SCFG_WI;
  94                break;
  95        case SND_SOC_DAIFMT_IB_IF:
  96                break;
  97        default:
  98                goto out;
  99        }
 100
 101        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 102        case SND_SOC_DAIFMT_CBM_CFM:    /* CODEC master */
 103                ct |= PSC_I2SCFG_MS;    /* PSC I2S slave mode */
 104                break;
 105        case SND_SOC_DAIFMT_CBS_CFS:    /* CODEC slave */
 106                ct &= ~PSC_I2SCFG_MS;   /* PSC I2S Master mode */
 107                break;
 108        default:
 109                goto out;
 110        }
 111
 112        pscdata->cfg = ct;
 113        ret = 0;
 114out:
 115        return ret;
 116}
 117
 118static int au1xpsc_i2s_hw_params(struct snd_pcm_substream *substream,
 119                                 struct snd_pcm_hw_params *params,
 120                                 struct snd_soc_dai *dai)
 121{
 122        struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
 123
 124        int cfgbits;
 125        unsigned long stat;
 126
 127        /* check if the PSC is already streaming data */
 128        stat = au_readl(I2S_STAT(pscdata));
 129        if (stat & (PSC_I2SSTAT_TB | PSC_I2SSTAT_RB)) {
 130                /* reject parameters not currently set up in hardware */
 131                cfgbits = au_readl(I2S_CFG(pscdata));
 132                if ((PSC_I2SCFG_GET_LEN(cfgbits) != params->msbits) ||
 133                    (params_rate(params) != pscdata->rate))
 134                        return -EINVAL;
 135        } else {
 136                /* set sample bitdepth */
 137                pscdata->cfg &= ~(0x1f << 4);
 138                pscdata->cfg |= PSC_I2SCFG_SET_LEN(params->msbits);
 139                /* remember current rate for other stream */
 140                pscdata->rate = params_rate(params);
 141        }
 142        return 0;
 143}
 144
 145/* Configure PSC late:  on my devel systems the codec  is I2S master and
 146 * supplies the i2sbitclock __AND__ i2sMclk (!) to the PSC unit.  ASoC
 147 * uses aggressive PM and  switches the codec off  when it is not in use
 148 * which also means the PSC unit doesn't get any clocks and is therefore
 149 * dead. That's why this chunk here gets called from the trigger callback
 150 * because I can be reasonably certain the codec is driving the clocks.
 151 */
 152static int au1xpsc_i2s_configure(struct au1xpsc_audio_data *pscdata)
 153{
 154        unsigned long tmo;
 155
 156        /* bring PSC out of sleep, and configure I2S unit */
 157        au_writel(PSC_CTRL_ENABLE, PSC_CTRL(pscdata));
 158        au_sync();
 159
 160        tmo = 1000000;
 161        while (!(au_readl(I2S_STAT(pscdata)) & PSC_I2SSTAT_SR) && tmo)
 162                tmo--;
 163
 164        if (!tmo)
 165                goto psc_err;
 166
 167        au_writel(0, I2S_CFG(pscdata));
 168        au_sync();
 169        au_writel(pscdata->cfg | PSC_I2SCFG_DE_ENABLE, I2S_CFG(pscdata));
 170        au_sync();
 171
 172        /* wait for I2S controller to become ready */
 173        tmo = 1000000;
 174        while (!(au_readl(I2S_STAT(pscdata)) & PSC_I2SSTAT_DR) && tmo)
 175                tmo--;
 176
 177        if (tmo)
 178                return 0;
 179
 180psc_err:
 181        au_writel(0, I2S_CFG(pscdata));
 182        au_writel(PSC_CTRL_SUSPEND, PSC_CTRL(pscdata));
 183        au_sync();
 184        return -ETIMEDOUT;
 185}
 186
 187static int au1xpsc_i2s_start(struct au1xpsc_audio_data *pscdata, int stype)
 188{
 189        unsigned long tmo, stat;
 190        int ret;
 191
 192        ret = 0;
 193
 194        /* if both TX and RX are idle, configure the PSC  */
 195        stat = au_readl(I2S_STAT(pscdata));
 196        if (!(stat & (PSC_I2SSTAT_TB | PSC_I2SSTAT_RB))) {
 197                ret = au1xpsc_i2s_configure(pscdata);
 198                if (ret)
 199                        goto out;
 200        }
 201
 202        au_writel(I2SPCR_CLRFIFO(stype), I2S_PCR(pscdata));
 203        au_sync();
 204        au_writel(I2SPCR_START(stype), I2S_PCR(pscdata));
 205        au_sync();
 206
 207        /* wait for start confirmation */
 208        tmo = 1000000;
 209        while (!(au_readl(I2S_STAT(pscdata)) & I2SSTAT_BUSY(stype)) && tmo)
 210                tmo--;
 211
 212        if (!tmo) {
 213                au_writel(I2SPCR_STOP(stype), I2S_PCR(pscdata));
 214                au_sync();
 215                ret = -ETIMEDOUT;
 216        }
 217out:
 218        return ret;
 219}
 220
 221static int au1xpsc_i2s_stop(struct au1xpsc_audio_data *pscdata, int stype)
 222{
 223        unsigned long tmo, stat;
 224
 225        au_writel(I2SPCR_STOP(stype), I2S_PCR(pscdata));
 226        au_sync();
 227
 228        /* wait for stop confirmation */
 229        tmo = 1000000;
 230        while ((au_readl(I2S_STAT(pscdata)) & I2SSTAT_BUSY(stype)) && tmo)
 231                tmo--;
 232
 233        /* if both TX and RX are idle, disable PSC */
 234        stat = au_readl(I2S_STAT(pscdata));
 235        if (!(stat & (PSC_I2SSTAT_TB | PSC_I2SSTAT_RB))) {
 236                au_writel(0, I2S_CFG(pscdata));
 237                au_sync();
 238                au_writel(PSC_CTRL_SUSPEND, PSC_CTRL(pscdata));
 239                au_sync();
 240        }
 241        return 0;
 242}
 243
 244static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
 245                               struct snd_soc_dai *dai)
 246{
 247        struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
 248        int ret, stype = SUBSTREAM_TYPE(substream);
 249
 250        switch (cmd) {
 251        case SNDRV_PCM_TRIGGER_START:
 252        case SNDRV_PCM_TRIGGER_RESUME:
 253                ret = au1xpsc_i2s_start(pscdata, stype);
 254                break;
 255        case SNDRV_PCM_TRIGGER_STOP:
 256        case SNDRV_PCM_TRIGGER_SUSPEND:
 257                ret = au1xpsc_i2s_stop(pscdata, stype);
 258                break;
 259        default:
 260                ret = -EINVAL;
 261        }
 262        return ret;
 263}
 264
 265static int au1xpsc_i2s_probe(struct platform_device *pdev,
 266                             struct snd_soc_dai *dai)
 267{
 268        struct resource *r;
 269        unsigned long sel;
 270        int ret;
 271
 272        if (au1xpsc_i2s_workdata)
 273                return -EBUSY;
 274
 275        au1xpsc_i2s_workdata =
 276                kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
 277        if (!au1xpsc_i2s_workdata)
 278                return -ENOMEM;
 279
 280        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 281        if (!r) {
 282                ret = -ENODEV;
 283                goto out0;
 284        }
 285
 286        ret = -EBUSY;
 287        au1xpsc_i2s_workdata->ioarea =
 288                request_mem_region(r->start, r->end - r->start + 1,
 289                                        "au1xpsc_i2s");
 290        if (!au1xpsc_i2s_workdata->ioarea)
 291                goto out0;
 292
 293        au1xpsc_i2s_workdata->mmio = ioremap(r->start, 0xffff);
 294        if (!au1xpsc_i2s_workdata->mmio)
 295                goto out1;
 296
 297        /* preserve PSC clock source set up by platform (dev.platform_data
 298         * is already occupied by soc layer)
 299         */
 300        sel = au_readl(PSC_SEL(au1xpsc_i2s_workdata)) & PSC_SEL_CLK_MASK;
 301        au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
 302        au_sync();
 303        au_writel(PSC_SEL_PS_I2SMODE | sel, PSC_SEL(au1xpsc_i2s_workdata));
 304        au_writel(0, I2S_CFG(au1xpsc_i2s_workdata));
 305        au_sync();
 306
 307        /* preconfigure: set max rx/tx fifo depths */
 308        au1xpsc_i2s_workdata->cfg |=
 309                        PSC_I2SCFG_RT_FIFO8 | PSC_I2SCFG_TT_FIFO8;
 310
 311        /* don't wait for I2S core to become ready now; clocks may not
 312         * be running yet; depending on clock input for PSC a wait might
 313         * time out.
 314         */
 315
 316        return 0;
 317
 318out1:
 319        release_resource(au1xpsc_i2s_workdata->ioarea);
 320        kfree(au1xpsc_i2s_workdata->ioarea);
 321out0:
 322        kfree(au1xpsc_i2s_workdata);
 323        au1xpsc_i2s_workdata = NULL;
 324        return ret;
 325}
 326
 327static void au1xpsc_i2s_remove(struct platform_device *pdev,
 328                               struct snd_soc_dai *dai)
 329{
 330        au_writel(0, I2S_CFG(au1xpsc_i2s_workdata));
 331        au_sync();
 332        au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
 333        au_sync();
 334
 335        iounmap(au1xpsc_i2s_workdata->mmio);
 336        release_resource(au1xpsc_i2s_workdata->ioarea);
 337        kfree(au1xpsc_i2s_workdata->ioarea);
 338        kfree(au1xpsc_i2s_workdata);
 339        au1xpsc_i2s_workdata = NULL;
 340}
 341
 342static int au1xpsc_i2s_suspend(struct snd_soc_dai *cpu_dai)
 343{
 344        /* save interesting register and disable PSC */
 345        au1xpsc_i2s_workdata->pm[0] =
 346                au_readl(PSC_SEL(au1xpsc_i2s_workdata));
 347
 348        au_writel(0, I2S_CFG(au1xpsc_i2s_workdata));
 349        au_sync();
 350        au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
 351        au_sync();
 352
 353        return 0;
 354}
 355
 356static int au1xpsc_i2s_resume(struct snd_soc_dai *cpu_dai)
 357{
 358        /* select I2S mode and PSC clock */
 359        au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
 360        au_sync();
 361        au_writel(0, PSC_SEL(au1xpsc_i2s_workdata));
 362        au_sync();
 363        au_writel(au1xpsc_i2s_workdata->pm[0],
 364                        PSC_SEL(au1xpsc_i2s_workdata));
 365        au_sync();
 366
 367        return 0;
 368}
 369
 370static struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = {
 371        .trigger        = au1xpsc_i2s_trigger,
 372        .hw_params      = au1xpsc_i2s_hw_params,
 373        .set_fmt        = au1xpsc_i2s_set_fmt,
 374};
 375
 376struct snd_soc_dai au1xpsc_i2s_dai = {
 377        .name                   = "au1xpsc_i2s",
 378        .probe                  = au1xpsc_i2s_probe,
 379        .remove                 = au1xpsc_i2s_remove,
 380        .suspend                = au1xpsc_i2s_suspend,
 381        .resume                 = au1xpsc_i2s_resume,
 382        .playback = {
 383                .rates          = AU1XPSC_I2S_RATES,
 384                .formats        = AU1XPSC_I2S_FMTS,
 385                .channels_min   = 2,
 386                .channels_max   = 8,    /* 2 without external help */
 387        },
 388        .capture = {
 389                .rates          = AU1XPSC_I2S_RATES,
 390                .formats        = AU1XPSC_I2S_FMTS,
 391                .channels_min   = 2,
 392                .channels_max   = 8,    /* 2 without external help */
 393        },
 394        .ops = &au1xpsc_i2s_dai_ops,
 395};
 396EXPORT_SYMBOL(au1xpsc_i2s_dai);
 397
 398static int __init au1xpsc_i2s_init(void)
 399{
 400        au1xpsc_i2s_workdata = NULL;
 401        return snd_soc_register_dai(&au1xpsc_i2s_dai);
 402}
 403
 404static void __exit au1xpsc_i2s_exit(void)
 405{
 406        snd_soc_unregister_dai(&au1xpsc_i2s_dai);
 407}
 408
 409module_init(au1xpsc_i2s_init);
 410module_exit(au1xpsc_i2s_exit);
 411
 412MODULE_LICENSE("GPL");
 413MODULE_DESCRIPTION("Au12x0/Au1550 PSC I2S ALSA ASoC audio driver");
 414MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
 415