linux/sound/soc/au1x/i2sc.c
<<
>>
Prefs
   1/*
   2 * Au1000/Au1500/Au1100 I2S controller driver for ASoC
   3 *
   4 * (c) 2011 Manuel Lauss <manuel.lauss@googlemail.com>
   5 *
   6 * Note: clock supplied to the I2S controller must be 256x samplerate.
   7 */
   8
   9#include <linux/init.h>
  10#include <linux/module.h>
  11#include <linux/slab.h>
  12#include <linux/suspend.h>
  13#include <sound/core.h>
  14#include <sound/pcm.h>
  15#include <sound/initval.h>
  16#include <sound/soc.h>
  17#include <asm/mach-au1x00/au1000.h>
  18
  19#include "psc.h"
  20
  21#define I2S_RXTX        0x00
  22#define I2S_CFG         0x04
  23#define I2S_ENABLE      0x08
  24
  25#define CFG_XU          (1 << 25)       /* tx underflow */
  26#define CFG_XO          (1 << 24)
  27#define CFG_RU          (1 << 23)
  28#define CFG_RO          (1 << 22)
  29#define CFG_TR          (1 << 21)
  30#define CFG_TE          (1 << 20)
  31#define CFG_TF          (1 << 19)
  32#define CFG_RR          (1 << 18)
  33#define CFG_RF          (1 << 17)
  34#define CFG_ICK         (1 << 12)       /* clock invert */
  35#define CFG_PD          (1 << 11)       /* set to make I2SDIO INPUT */
  36#define CFG_LB          (1 << 10)       /* loopback */
  37#define CFG_IC          (1 << 9)        /* word select invert */
  38#define CFG_FM_I2S      (0 << 7)        /* I2S format */
  39#define CFG_FM_LJ       (1 << 7)        /* left-justified */
  40#define CFG_FM_RJ       (2 << 7)        /* right-justified */
  41#define CFG_FM_MASK     (3 << 7)
  42#define CFG_TN          (1 << 6)        /* tx fifo en */
  43#define CFG_RN          (1 << 5)        /* rx fifo en */
  44#define CFG_SZ_8        (0x08)
  45#define CFG_SZ_16       (0x10)
  46#define CFG_SZ_18       (0x12)
  47#define CFG_SZ_20       (0x14)
  48#define CFG_SZ_24       (0x18)
  49#define CFG_SZ_MASK     (0x1f)
  50#define EN_D            (1 << 1)        /* DISable */
  51#define EN_CE           (1 << 0)        /* clock enable */
  52
  53/* only limited by clock generator and board design */
  54#define AU1XI2SC_RATES \
  55        SNDRV_PCM_RATE_CONTINUOUS
  56
  57#define AU1XI2SC_FMTS \
  58        (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |            \
  59        SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |     \
  60        SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE |     \
  61        SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_U18_3LE |   \
  62        SNDRV_PCM_FMTBIT_S18_3BE | SNDRV_PCM_FMTBIT_U18_3BE |   \
  63        SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE |   \
  64        SNDRV_PCM_FMTBIT_S20_3BE | SNDRV_PCM_FMTBIT_U20_3BE |   \
  65        SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |     \
  66        SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_U24_BE |     \
  67        0)
  68
  69static inline unsigned long RD(struct au1xpsc_audio_data *ctx, int reg)
  70{
  71        return __raw_readl(ctx->mmio + reg);
  72}
  73
  74static inline void WR(struct au1xpsc_audio_data *ctx, int reg, unsigned long v)
  75{
  76        __raw_writel(v, ctx->mmio + reg);
  77        wmb();
  78}
  79
  80static int au1xi2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
  81{
  82        struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(cpu_dai);
  83        unsigned long c;
  84        int ret;
  85
  86        ret = -EINVAL;
  87        c = ctx->cfg;
  88
  89        c &= ~CFG_FM_MASK;
  90        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
  91        case SND_SOC_DAIFMT_I2S:
  92                c |= CFG_FM_I2S;
  93                break;
  94        case SND_SOC_DAIFMT_MSB:
  95                c |= CFG_FM_RJ;
  96                break;
  97        case SND_SOC_DAIFMT_LSB:
  98                c |= CFG_FM_LJ;
  99                break;
 100        default:
 101                goto out;
 102        }
 103
 104        c &= ~(CFG_IC | CFG_ICK);               /* IB-IF */
 105        switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
 106        case SND_SOC_DAIFMT_NB_NF:
 107                c |= CFG_IC | CFG_ICK;
 108                break;
 109        case SND_SOC_DAIFMT_NB_IF:
 110                c |= CFG_IC;
 111                break;
 112        case SND_SOC_DAIFMT_IB_NF:
 113                c |= CFG_ICK;
 114                break;
 115        case SND_SOC_DAIFMT_IB_IF:
 116                break;
 117        default:
 118                goto out;
 119        }
 120
 121        /* I2S controller only supports master */
 122        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 123        case SND_SOC_DAIFMT_CBS_CFS:    /* CODEC slave */
 124                break;
 125        default:
 126                goto out;
 127        }
 128
 129        ret = 0;
 130        ctx->cfg = c;
 131out:
 132        return ret;
 133}
 134
 135static int au1xi2s_trigger(struct snd_pcm_substream *substream,
 136                           int cmd, struct snd_soc_dai *dai)
 137{
 138        struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai);
 139        int stype = SUBSTREAM_TYPE(substream);
 140
 141        switch (cmd) {
 142        case SNDRV_PCM_TRIGGER_START:
 143        case SNDRV_PCM_TRIGGER_RESUME:
 144                /* power up */
 145                WR(ctx, I2S_ENABLE, EN_D | EN_CE);
 146                WR(ctx, I2S_ENABLE, EN_CE);
 147                ctx->cfg |= (stype == PCM_TX) ? CFG_TN : CFG_RN;
 148                WR(ctx, I2S_CFG, ctx->cfg);
 149                break;
 150        case SNDRV_PCM_TRIGGER_STOP:
 151        case SNDRV_PCM_TRIGGER_SUSPEND:
 152                ctx->cfg &= ~((stype == PCM_TX) ? CFG_TN : CFG_RN);
 153                WR(ctx, I2S_CFG, ctx->cfg);
 154                WR(ctx, I2S_ENABLE, EN_D);              /* power off */
 155                break;
 156        default:
 157                return -EINVAL;
 158        }
 159
 160        return 0;
 161}
 162
 163static unsigned long msbits_to_reg(int msbits)
 164{
 165        switch (msbits) {
 166        case 8:
 167                return CFG_SZ_8;
 168        case 16:
 169                return CFG_SZ_16;
 170        case 18:
 171                return CFG_SZ_18;
 172        case 20:
 173                return CFG_SZ_20;
 174        case 24:
 175                return CFG_SZ_24;
 176        }
 177        return 0;
 178}
 179
 180static int au1xi2s_hw_params(struct snd_pcm_substream *substream,
 181                             struct snd_pcm_hw_params *params,
 182                             struct snd_soc_dai *dai)
 183{
 184        struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai);
 185        unsigned long v;
 186
 187        v = msbits_to_reg(params->msbits);
 188        if (!v)
 189                return -EINVAL;
 190
 191        ctx->cfg &= ~CFG_SZ_MASK;
 192        ctx->cfg |= v;
 193        return 0;
 194}
 195
 196static int au1xi2s_startup(struct snd_pcm_substream *substream,
 197                           struct snd_soc_dai *dai)
 198{
 199        struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai);
 200        snd_soc_dai_set_dma_data(dai, substream, &ctx->dmaids[0]);
 201        return 0;
 202}
 203
 204static const struct snd_soc_dai_ops au1xi2s_dai_ops = {
 205        .startup        = au1xi2s_startup,
 206        .trigger        = au1xi2s_trigger,
 207        .hw_params      = au1xi2s_hw_params,
 208        .set_fmt        = au1xi2s_set_fmt,
 209};
 210
 211static struct snd_soc_dai_driver au1xi2s_dai_driver = {
 212        .symmetric_rates        = 1,
 213        .playback = {
 214                .rates          = AU1XI2SC_RATES,
 215                .formats        = AU1XI2SC_FMTS,
 216                .channels_min   = 2,
 217                .channels_max   = 2,
 218        },
 219        .capture = {
 220                .rates          = AU1XI2SC_RATES,
 221                .formats        = AU1XI2SC_FMTS,
 222                .channels_min   = 2,
 223                .channels_max   = 2,
 224        },
 225        .ops = &au1xi2s_dai_ops,
 226};
 227
 228static const struct snd_soc_component_driver au1xi2s_component = {
 229        .name           = "au1xi2s",
 230};
 231
 232static int au1xi2s_drvprobe(struct platform_device *pdev)
 233{
 234        struct resource *iores, *dmares;
 235        struct au1xpsc_audio_data *ctx;
 236
 237        ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
 238        if (!ctx)
 239                return -ENOMEM;
 240
 241        iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 242        if (!iores)
 243                return -ENODEV;
 244
 245        if (!devm_request_mem_region(&pdev->dev, iores->start,
 246                                     resource_size(iores),
 247                                     pdev->name))
 248                return -EBUSY;
 249
 250        ctx->mmio = devm_ioremap_nocache(&pdev->dev, iores->start,
 251                                         resource_size(iores));
 252        if (!ctx->mmio)
 253                return -EBUSY;
 254
 255        dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
 256        if (!dmares)
 257                return -EBUSY;
 258        ctx->dmaids[SNDRV_PCM_STREAM_PLAYBACK] = dmares->start;
 259
 260        dmares = platform_get_resource(pdev, IORESOURCE_DMA, 1);
 261        if (!dmares)
 262                return -EBUSY;
 263        ctx->dmaids[SNDRV_PCM_STREAM_CAPTURE] = dmares->start;
 264
 265        platform_set_drvdata(pdev, ctx);
 266
 267        return snd_soc_register_component(&pdev->dev, &au1xi2s_component,
 268                                          &au1xi2s_dai_driver, 1);
 269}
 270
 271static int au1xi2s_drvremove(struct platform_device *pdev)
 272{
 273        struct au1xpsc_audio_data *ctx = platform_get_drvdata(pdev);
 274
 275        snd_soc_unregister_component(&pdev->dev);
 276
 277        WR(ctx, I2S_ENABLE, EN_D);      /* clock off, disable */
 278
 279        return 0;
 280}
 281
 282#ifdef CONFIG_PM
 283static int au1xi2s_drvsuspend(struct device *dev)
 284{
 285        struct au1xpsc_audio_data *ctx = dev_get_drvdata(dev);
 286
 287        WR(ctx, I2S_ENABLE, EN_D);      /* clock off, disable */
 288
 289        return 0;
 290}
 291
 292static int au1xi2s_drvresume(struct device *dev)
 293{
 294        return 0;
 295}
 296
 297static const struct dev_pm_ops au1xi2sc_pmops = {
 298        .suspend        = au1xi2s_drvsuspend,
 299        .resume         = au1xi2s_drvresume,
 300};
 301
 302#define AU1XI2SC_PMOPS (&au1xi2sc_pmops)
 303
 304#else
 305
 306#define AU1XI2SC_PMOPS NULL
 307
 308#endif
 309
 310static struct platform_driver au1xi2s_driver = {
 311        .driver = {
 312                .name   = "alchemy-i2sc",
 313                .owner  = THIS_MODULE,
 314                .pm     = AU1XI2SC_PMOPS,
 315        },
 316        .probe          = au1xi2s_drvprobe,
 317        .remove         = au1xi2s_drvremove,
 318};
 319
 320module_platform_driver(au1xi2s_driver);
 321
 322MODULE_LICENSE("GPL");
 323MODULE_DESCRIPTION("Au1000/1500/1100 I2S ASoC driver");
 324MODULE_AUTHOR("Manuel Lauss");
 325