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