linux/sound/soc/adi/axi-i2s.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2012-2013, Analog Devices Inc.
   3 * Author: Lars-Peter Clausen <lars@metafoo.de>
   4 *
   5 * Licensed under the GPL-2.
   6 */
   7
   8#include <linux/clk.h>
   9#include <linux/init.h>
  10#include <linux/kernel.h>
  11#include <linux/module.h>
  12#include <linux/of.h>
  13#include <linux/platform_device.h>
  14#include <linux/regmap.h>
  15#include <linux/slab.h>
  16
  17#include <sound/core.h>
  18#include <sound/pcm.h>
  19#include <sound/pcm_params.h>
  20#include <sound/soc.h>
  21#include <sound/dmaengine_pcm.h>
  22
  23#define AXI_I2S_REG_RESET       0x00
  24#define AXI_I2S_REG_CTRL        0x04
  25#define AXI_I2S_REG_CLK_CTRL    0x08
  26#define AXI_I2S_REG_STATUS      0x10
  27
  28#define AXI_I2S_REG_RX_FIFO     0x28
  29#define AXI_I2S_REG_TX_FIFO     0x2C
  30
  31#define AXI_I2S_RESET_GLOBAL    BIT(0)
  32#define AXI_I2S_RESET_TX_FIFO   BIT(1)
  33#define AXI_I2S_RESET_RX_FIFO   BIT(2)
  34
  35#define AXI_I2S_CTRL_TX_EN      BIT(0)
  36#define AXI_I2S_CTRL_RX_EN      BIT(1)
  37
  38/* The frame size is configurable, but for now we always set it 64 bit */
  39#define AXI_I2S_BITS_PER_FRAME 64
  40
  41struct axi_i2s {
  42        struct regmap *regmap;
  43        struct clk *clk;
  44        struct clk *clk_ref;
  45
  46        struct snd_soc_dai_driver dai_driver;
  47
  48        struct snd_dmaengine_dai_dma_data capture_dma_data;
  49        struct snd_dmaengine_dai_dma_data playback_dma_data;
  50
  51        struct snd_ratnum ratnum;
  52        struct snd_pcm_hw_constraint_ratnums rate_constraints;
  53};
  54
  55static int axi_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
  56        struct snd_soc_dai *dai)
  57{
  58        struct axi_i2s *i2s = snd_soc_dai_get_drvdata(dai);
  59        unsigned int mask, val;
  60
  61        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
  62                mask = AXI_I2S_CTRL_RX_EN;
  63        else
  64                mask = AXI_I2S_CTRL_TX_EN;
  65
  66        switch (cmd) {
  67        case SNDRV_PCM_TRIGGER_START:
  68        case SNDRV_PCM_TRIGGER_RESUME:
  69        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
  70                val = mask;
  71                break;
  72        case SNDRV_PCM_TRIGGER_STOP:
  73        case SNDRV_PCM_TRIGGER_SUSPEND:
  74        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
  75                val = 0;
  76                break;
  77        default:
  78                return -EINVAL;
  79        }
  80
  81        regmap_update_bits(i2s->regmap, AXI_I2S_REG_CTRL, mask, val);
  82
  83        return 0;
  84}
  85
  86static int axi_i2s_hw_params(struct snd_pcm_substream *substream,
  87        struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
  88{
  89        struct axi_i2s *i2s = snd_soc_dai_get_drvdata(dai);
  90        unsigned int bclk_div, word_size;
  91        unsigned int bclk_rate;
  92
  93        bclk_rate = params_rate(params) * AXI_I2S_BITS_PER_FRAME;
  94
  95        word_size = AXI_I2S_BITS_PER_FRAME / 2 - 1;
  96        bclk_div = DIV_ROUND_UP(clk_get_rate(i2s->clk_ref), bclk_rate) / 2 - 1;
  97
  98        regmap_write(i2s->regmap, AXI_I2S_REG_CLK_CTRL, (word_size << 16) |
  99                bclk_div);
 100
 101        return 0;
 102}
 103
 104static int axi_i2s_startup(struct snd_pcm_substream *substream,
 105        struct snd_soc_dai *dai)
 106{
 107        struct axi_i2s *i2s = snd_soc_dai_get_drvdata(dai);
 108        uint32_t mask;
 109        int ret;
 110
 111        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
 112                mask = AXI_I2S_RESET_RX_FIFO;
 113        else
 114                mask = AXI_I2S_RESET_TX_FIFO;
 115
 116        regmap_write(i2s->regmap, AXI_I2S_REG_RESET, mask);
 117
 118        ret = snd_pcm_hw_constraint_ratnums(substream->runtime, 0,
 119                           SNDRV_PCM_HW_PARAM_RATE,
 120                           &i2s->rate_constraints);
 121        if (ret)
 122                return ret;
 123
 124        return clk_prepare_enable(i2s->clk_ref);
 125}
 126
 127static void axi_i2s_shutdown(struct snd_pcm_substream *substream,
 128        struct snd_soc_dai *dai)
 129{
 130        struct axi_i2s *i2s = snd_soc_dai_get_drvdata(dai);
 131
 132        clk_disable_unprepare(i2s->clk_ref);
 133}
 134
 135static int axi_i2s_dai_probe(struct snd_soc_dai *dai)
 136{
 137        struct axi_i2s *i2s = snd_soc_dai_get_drvdata(dai);
 138
 139        snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data,
 140                &i2s->capture_dma_data);
 141
 142        return 0;
 143}
 144
 145static const struct snd_soc_dai_ops axi_i2s_dai_ops = {
 146        .startup = axi_i2s_startup,
 147        .shutdown = axi_i2s_shutdown,
 148        .trigger = axi_i2s_trigger,
 149        .hw_params = axi_i2s_hw_params,
 150};
 151
 152static struct snd_soc_dai_driver axi_i2s_dai = {
 153        .probe = axi_i2s_dai_probe,
 154        .playback = {
 155                .channels_min = 2,
 156                .channels_max = 2,
 157                .rates = SNDRV_PCM_RATE_KNOT,
 158                .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE,
 159        },
 160        .capture = {
 161                .channels_min = 2,
 162                .channels_max = 2,
 163                .rates = SNDRV_PCM_RATE_KNOT,
 164                .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE,
 165        },
 166        .ops = &axi_i2s_dai_ops,
 167        .symmetric_rates = 1,
 168};
 169
 170static const struct snd_soc_component_driver axi_i2s_component = {
 171        .name = "axi-i2s",
 172};
 173
 174static const struct regmap_config axi_i2s_regmap_config = {
 175        .reg_bits = 32,
 176        .reg_stride = 4,
 177        .val_bits = 32,
 178        .max_register = AXI_I2S_REG_STATUS,
 179};
 180
 181static int axi_i2s_probe(struct platform_device *pdev)
 182{
 183        struct resource *res;
 184        struct axi_i2s *i2s;
 185        void __iomem *base;
 186        int ret;
 187
 188        i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
 189        if (!i2s)
 190                return -ENOMEM;
 191
 192        platform_set_drvdata(pdev, i2s);
 193
 194        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 195        base = devm_ioremap_resource(&pdev->dev, res);
 196        if (IS_ERR(base))
 197                return PTR_ERR(base);
 198
 199        i2s->regmap = devm_regmap_init_mmio(&pdev->dev, base,
 200                &axi_i2s_regmap_config);
 201        if (IS_ERR(i2s->regmap))
 202                return PTR_ERR(i2s->regmap);
 203
 204        i2s->clk = devm_clk_get(&pdev->dev, "axi");
 205        if (IS_ERR(i2s->clk))
 206                return PTR_ERR(i2s->clk);
 207
 208        i2s->clk_ref = devm_clk_get(&pdev->dev, "ref");
 209        if (IS_ERR(i2s->clk_ref))
 210                return PTR_ERR(i2s->clk_ref);
 211
 212        ret = clk_prepare_enable(i2s->clk);
 213        if (ret)
 214                return ret;
 215
 216        i2s->playback_dma_data.addr = res->start + AXI_I2S_REG_TX_FIFO;
 217        i2s->playback_dma_data.addr_width = 4;
 218        i2s->playback_dma_data.maxburst = 1;
 219
 220        i2s->capture_dma_data.addr = res->start + AXI_I2S_REG_RX_FIFO;
 221        i2s->capture_dma_data.addr_width = 4;
 222        i2s->capture_dma_data.maxburst = 1;
 223
 224        i2s->ratnum.num = clk_get_rate(i2s->clk_ref) / 2 / AXI_I2S_BITS_PER_FRAME;
 225        i2s->ratnum.den_step = 1;
 226        i2s->ratnum.den_min = 1;
 227        i2s->ratnum.den_max = 64;
 228
 229        i2s->rate_constraints.rats = &i2s->ratnum;
 230        i2s->rate_constraints.nrats = 1;
 231
 232        regmap_write(i2s->regmap, AXI_I2S_REG_RESET, AXI_I2S_RESET_GLOBAL);
 233
 234        ret = devm_snd_soc_register_component(&pdev->dev, &axi_i2s_component,
 235                                         &axi_i2s_dai, 1);
 236        if (ret)
 237                goto err_clk_disable;
 238
 239        ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
 240        if (ret)
 241                goto err_clk_disable;
 242
 243        return 0;
 244
 245err_clk_disable:
 246        clk_disable_unprepare(i2s->clk);
 247        return ret;
 248}
 249
 250static int axi_i2s_dev_remove(struct platform_device *pdev)
 251{
 252        struct axi_i2s *i2s = platform_get_drvdata(pdev);
 253
 254        clk_disable_unprepare(i2s->clk);
 255
 256        return 0;
 257}
 258
 259static const struct of_device_id axi_i2s_of_match[] = {
 260        { .compatible = "adi,axi-i2s-1.00.a", },
 261        {},
 262};
 263MODULE_DEVICE_TABLE(of, axi_i2s_of_match);
 264
 265static struct platform_driver axi_i2s_driver = {
 266        .driver = {
 267                .name = "axi-i2s",
 268                .of_match_table = axi_i2s_of_match,
 269        },
 270        .probe = axi_i2s_probe,
 271        .remove = axi_i2s_dev_remove,
 272};
 273module_platform_driver(axi_i2s_driver);
 274
 275MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
 276MODULE_DESCRIPTION("AXI I2S driver");
 277MODULE_LICENSE("GPL");
 278